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:
dalbodeule
2024-08-04 20:27:54 +09:00
parent 514ab14c3c
commit 6da0662e2a
8 changed files with 47 additions and 138 deletions

View File

@@ -1,148 +0,0 @@
package space.mori.chzzk_bot.chatbot.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

@@ -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) {

View File

@@ -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
)
)
}
}
}
}
}
}

View File

@@ -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")

View File

@@ -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()