mirror of
https://github.com/dalbodeule/chibot-chzzk-bot.git
synced 2025-06-09 07:18:22 +00:00
Fix /user/{uid} endoints.
- ChzzkApis.kt moved to common - ChzzkApis.kt response data is nullable data. - if require {uid}'s data, getStreamInfo function called.
This commit is contained in:
parent
514ab14c3c
commit
6da0662e2a
@ -14,8 +14,7 @@ import space.mori.chzzk_bot.common.models.User
|
||||
import space.mori.chzzk_bot.common.services.LiveStatusService
|
||||
import space.mori.chzzk_bot.common.services.TimerConfigService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
import space.mori.chzzk_bot.common.utils.convertChzzkDateToLocalDateTime
|
||||
import space.mori.chzzk_bot.common.utils.getUptime
|
||||
import space.mori.chzzk_bot.common.utils.*
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatEventListener
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChzzkChat
|
||||
@ -42,7 +41,7 @@ object ChzzkHandler {
|
||||
|
||||
handlers.forEach { handler ->
|
||||
val streamInfo = getStreamInfo(handler.listener.channelId)
|
||||
if (streamInfo.content.status == "OPEN") handler.isActive(true, streamInfo)
|
||||
if (streamInfo.content?.status == "OPEN") handler.isActive(true, streamInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,8 +75,8 @@ object ChzzkHandler {
|
||||
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)
|
||||
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) {
|
||||
@ -152,14 +151,14 @@ class UserHandler(
|
||||
internal val isActive: Boolean
|
||||
get() = _isActive
|
||||
|
||||
internal fun isActive(value: Boolean, status: IData<IStreamInfo>) {
|
||||
internal fun isActive(value: Boolean, status: IData<IStreamInfo?>) {
|
||||
if(value) {
|
||||
logger.info("${user.username} is live.")
|
||||
|
||||
logger.info("ChzzkChat connecting... ${channel.channelName} - ${channel.channelId}")
|
||||
listener.connectBlocking()
|
||||
|
||||
streamStartTime = convertChzzkDateToLocalDateTime(status.content.openDate)
|
||||
streamStartTime = status.content?.openDate?.let { convertChzzkDateToLocalDateTime(it) }
|
||||
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
when(TimerConfigService.getConfig(UserService.getUser(channel.channelId)!!)?.option) {
|
||||
|
@ -1,16 +1,7 @@
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
import org.slf4j.LoggerFactory
|
||||
import space.mori.chzzk_bot.common.events.CoroutinesEventBus
|
||||
import space.mori.chzzk_bot.common.events.GetUserEvents
|
||||
import space.mori.chzzk_bot.common.events.GetUserType
|
||||
import space.mori.chzzk_bot.common.services.LiveStatusService
|
||||
import space.mori.chzzk_bot.common.services.UserService
|
||||
import xyz.r2turntrue.chzzk4j.Chzzk
|
||||
import xyz.r2turntrue.chzzk4j.ChzzkBuilder
|
||||
import xyz.r2turntrue.chzzk4j.types.channel.ChzzkChannel
|
||||
@ -24,34 +15,10 @@ object Connector {
|
||||
.withAuthorization(dotenv["NID_AUT"], dotenv["NID_SES"])
|
||||
.build()
|
||||
private val logger = LoggerFactory.getLogger(this::class.java)
|
||||
private val dispatcher: CoroutinesEventBus by inject(CoroutinesEventBus::class.java)
|
||||
|
||||
fun getChannel(channelId: String): ChzzkChannel? = chzzk.getChannel(channelId)
|
||||
|
||||
|
||||
init {
|
||||
logger.info("chzzk logged: ${chzzk.isLoggedIn} / ${chzzk.loggedUser?.nickname ?: "----"}")
|
||||
dispatcher.subscribe(GetUserEvents::class) {
|
||||
if (it.type == GetUserType.REQUEST) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
val channel = getChannel(it.uid ?: "")
|
||||
if(channel == null) dispatcher.post(GetUserEvents(
|
||||
GetUserType.NOTFOUND, null, null, null, null
|
||||
))
|
||||
else {
|
||||
val user = UserService.getUser(channel.channelId)
|
||||
dispatcher.post(
|
||||
GetUserEvents(
|
||||
GetUserType.RESPONSE,
|
||||
channel.channelId,
|
||||
channel.channelName,
|
||||
LiveStatusService.getLiveStatus(user!!)?.status ?: false,
|
||||
channel.channelImageUrl
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -7,6 +7,7 @@ import org.koin.java.KoinJavaComponent.inject
|
||||
import space.mori.chzzk_bot.common.events.*
|
||||
import space.mori.chzzk_bot.common.models.User
|
||||
import space.mori.chzzk_bot.common.services.*
|
||||
import space.mori.chzzk_bot.common.utils.getFollowDate
|
||||
import space.mori.chzzk_bot.common.utils.getUptime
|
||||
import space.mori.chzzk_bot.common.utils.getYoutubeVideo
|
||||
import xyz.r2turntrue.chzzk4j.chat.ChatMessage
|
||||
@ -280,7 +281,7 @@ class MessageHandler(
|
||||
result = followPattern.replace(result) { _ ->
|
||||
try {
|
||||
val followingDate = getFollowDate(listener.chatId, msg.userId)
|
||||
.content.streamingProperty.following?.followDate
|
||||
.content?.streamingProperty?.following?.followDate
|
||||
|
||||
val period = followingDate?.let {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
|
@ -12,8 +12,8 @@ import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEve
|
||||
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.common.utils.IData
|
||||
import space.mori.chzzk_bot.common.utils.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
|
||||
@ -33,18 +33,19 @@ class Discord: ListenerAdapter() {
|
||||
internal fun getChannel(guildId: Long, channelId: Long) =
|
||||
bot.getGuildById(guildId)?.getTextChannelById(channelId)
|
||||
|
||||
fun sendDiscord(user: User, status: IData<IStreamInfo>) {
|
||||
fun sendDiscord(user: User, status: IData<IStreamInfo?>) {
|
||||
if(status.content == null) return
|
||||
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.setTitle(status.content!!.liveTitle, "https://chzzk.naver.com/live/${user.token}")
|
||||
embed.setDescription("${user.username} 님이 방송을 시작했습니다.")
|
||||
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"))
|
||||
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()
|
||||
|
@ -1,17 +0,0 @@
|
||||
package space.mori.chzzk_bot.common.events
|
||||
|
||||
enum class GetUserType(var value: Int) {
|
||||
REQUEST(0),
|
||||
RESPONSE(1),
|
||||
NOTFOUND(2)
|
||||
}
|
||||
|
||||
class GetUserEvents(
|
||||
val type: GetUserType,
|
||||
val uid: String?,
|
||||
val nickname: String?,
|
||||
val isStreamOn: Boolean?,
|
||||
val avatarUrl: String?,
|
||||
): Event {
|
||||
var TAG = javaClass.simpleName
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
package space.mori.chzzk_bot.chatbot.chzzk
|
||||
package space.mori.chzzk_bot.common.utils
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@ -108,7 +108,7 @@ val client = OkHttpClient.Builder()
|
||||
.build()
|
||||
val gson = Gson()
|
||||
|
||||
fun getFollowDate(chatID: String, userId: String) : IData<IFollowContent> {
|
||||
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)
|
||||
@ -118,7 +118,7 @@ fun getFollowDate(chatID: String, userId: String) : IData<IFollowContent> {
|
||||
try {
|
||||
if(!response.isSuccessful) throw IOException("Unexpected code ${response.code}")
|
||||
val body = response.body?.string()
|
||||
val follow = gson.fromJson(body, object: TypeToken<IData<IFollowContent>>() {})
|
||||
val follow = gson.fromJson(body, object: TypeToken<IData<IFollowContent?>>() {})
|
||||
|
||||
return follow
|
||||
} catch(e: Exception) {
|
||||
@ -128,7 +128,7 @@ fun getFollowDate(chatID: String, userId: String) : IData<IFollowContent> {
|
||||
}
|
||||
}
|
||||
|
||||
fun getStreamInfo(userId: String) : IData<IStreamInfo> {
|
||||
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)
|
||||
@ -138,7 +138,7 @@ fun getStreamInfo(userId: String) : IData<IStreamInfo> {
|
||||
try {
|
||||
if(!response.isSuccessful) throw IOException("Unexpected code ${response.code}")
|
||||
val body = response.body?.string()
|
||||
val follow = gson.fromJson(body, object: TypeToken<IData<IStreamInfo>>() {})
|
||||
val follow = gson.fromJson(body, object: TypeToken<IData<IStreamInfo?>>() {})
|
||||
|
||||
return follow
|
||||
} catch(e: Exception) {
|
@ -1,10 +1,8 @@
|
||||
package space.mori.chzzk_bot.common.utils
|
||||
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.JsonObject
|
||||
import io.github.cdimascio.dotenv.dotenv
|
||||
import okhttp3.HttpUrl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import java.io.IOException
|
||||
|
||||
@ -18,9 +16,6 @@ data class YoutubeVideo(
|
||||
val regex = ".*(?:youtu.be/|v/|u/\\w/|embed/|watch\\?v=|&v=)([^#&?]*).*".toRegex()
|
||||
val durationRegex = """PT(\d+H)?(\d+M)?(\d+S)?""".toRegex()
|
||||
|
||||
val client = OkHttpClient()
|
||||
val gson = Gson()
|
||||
|
||||
val dotenv = dotenv {
|
||||
ignoreIfMissing = true
|
||||
}
|
||||
|
@ -4,16 +4,8 @@ import io.ktor.http.*
|
||||
import io.ktor.server.application.*
|
||||
import io.ktor.server.response.*
|
||||
import io.ktor.server.routing.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.serialization.Serializable
|
||||
import org.koin.java.KoinJavaComponent.inject
|
||||
import space.mori.chzzk_bot.common.events.CoroutinesEventBus
|
||||
import space.mori.chzzk_bot.common.events.GetUserEvents
|
||||
import space.mori.chzzk_bot.common.events.GetUserType
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import space.mori.chzzk_bot.common.utils.getStreamInfo
|
||||
|
||||
@Serializable
|
||||
data class GetUserDTO(
|
||||
@ -23,33 +15,11 @@ data class GetUserDTO(
|
||||
val avatarUrl: String,
|
||||
)
|
||||
|
||||
fun GetUserEvents.toDTO(): GetUserDTO {
|
||||
return GetUserDTO(
|
||||
this.uid!!,
|
||||
this.nickname!!,
|
||||
this.isStreamOn!!,
|
||||
this.avatarUrl!!
|
||||
)
|
||||
}
|
||||
|
||||
fun Routing.apiRoutes() {
|
||||
val dispatcher: CoroutinesEventBus by inject(CoroutinesEventBus::class.java)
|
||||
val callMap = ConcurrentHashMap<String, ConcurrentLinkedQueue<ApplicationCall>>()
|
||||
|
||||
fun addCall(uid: String, call: ApplicationCall) {
|
||||
callMap.computeIfAbsent(uid) { ConcurrentLinkedQueue() }.add(call)
|
||||
}
|
||||
|
||||
fun removeCall(uid: String, call: ApplicationCall) {
|
||||
callMap[uid]?.remove(call)
|
||||
if(callMap[uid]?.isEmpty() == true) {
|
||||
callMap.remove(uid)
|
||||
}
|
||||
}
|
||||
|
||||
route("/") {
|
||||
get {
|
||||
call.respondText("Hello World!", status = HttpStatusCode.OK)
|
||||
call.respondText("Hello World!", status =
|
||||
HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
route("/health") {
|
||||
@ -57,38 +27,31 @@ fun Routing.apiRoutes() {
|
||||
call.respondText("OK", status= HttpStatusCode.OK)
|
||||
}
|
||||
}
|
||||
|
||||
route("/user/{uid}") {
|
||||
get {
|
||||
val uid = call.parameters["uid"]
|
||||
if(uid == null) {
|
||||
call.respondText("Require UID", status = HttpStatusCode.NotFound)
|
||||
return@get
|
||||
}
|
||||
val user = getStreamInfo(uid)
|
||||
if(user.content == null) {
|
||||
call.respondText("User not found", status = HttpStatusCode.NotFound)
|
||||
return@get
|
||||
} else {
|
||||
call.respond(HttpStatusCode.OK, GetUserDTO(
|
||||
user.content!!.channel.channelId,
|
||||
user.content!!.channel.channelName,
|
||||
user.content!!.status == "OPEN",
|
||||
user.content!!.channel.channelImageUrl
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
route("/user") {
|
||||
get {
|
||||
call.respondText("Require UID", status = HttpStatusCode.NotFound)
|
||||
}
|
||||
get("{uid}") {
|
||||
val uid = call.parameters["uid"]
|
||||
if(uid != null) {
|
||||
addCall(uid, call)
|
||||
if(!callMap.containsKey(uid)) {
|
||||
CoroutineScope(Dispatchers.Default).launch {
|
||||
dispatcher.post(GetUserEvents(GetUserType.REQUEST, null, null, null, null))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dispatcher.subscribe(GetUserEvents::class) {
|
||||
if(it.type == GetUserType.REQUEST) return@subscribe
|
||||
|
||||
CoroutineScope(Dispatchers.Default). launch {
|
||||
if (it.type == GetUserType.NOTFOUND) {
|
||||
callMap[it.uid]?.forEach { call ->
|
||||
call.respondText("User not found", status = HttpStatusCode.NotFound)
|
||||
removeCall(it.uid ?: "", call)
|
||||
}
|
||||
return@launch
|
||||
}
|
||||
callMap[it.uid]?.forEach { call ->
|
||||
call.respond(HttpStatusCode.OK, it.toDTO())
|
||||
removeCall(it.uid ?: "", call)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user