login method changed to chzzk login

This commit is contained in:
dalbodeule 2025-01-08 23:13:04 +09:00
parent eccf1a29bc
commit d3ed6c2d86
No known key found for this signature in database
GPG Key ID: EFA860D069C9FA65
10 changed files with 175 additions and 118 deletions

View File

@ -39,7 +39,7 @@ object ChzzkHandler {
fun enable() { fun enable() {
botUid = chzzk.loggedUser.userId botUid = chzzk.loggedUser.userId
UserService.getAllUsers().map { UserService.getAllUsers().map {
if(it.token != null && !it.isDisabled) if(!it.isDisabled)
chzzk.getChannel(it.token)?.let { token -> addUser(token, it) } chzzk.getChannel(it.token)?.let { token -> addUser(token, it) }
} }

View File

@ -42,7 +42,7 @@ class MessageHandler(
if(it.type == SongType.STREAM_OFF) { if(it.type == SongType.STREAM_OFF) {
val user = UserService.getUser(channel.channelId) val user = UserService.getUser(channel.channelId)
if(! user?.let { usr -> SongListService.getSong(usr) }.isNullOrEmpty()) { if(! user?.let { usr -> SongListService.getSong(usr) }.isNullOrEmpty()) {
SongListService.deleteUser(user!!) SongListService.deleteUser(user)
} }
} }
} }
@ -161,7 +161,7 @@ class MessageHandler(
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
dispatcher.post( dispatcher.post(
TimerEvent( TimerEvent(
user.token!!, user.token,
TimerType.UPTIME, TimerType.UPTIME,
getUptime(handler.streamStartTime!!) getUptime(handler.streamStartTime!!)
) )
@ -171,7 +171,7 @@ class MessageHandler(
"삭제" -> { "삭제" -> {
logger.debug("${user.token} / 삭제") logger.debug("${user.token} / 삭제")
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
dispatcher.post(TimerEvent(user.token!!, TimerType.REMOVE, "")) dispatcher.post(TimerEvent(user.token, TimerType.REMOVE, ""))
} }
} }
"설정" -> { "설정" -> {
@ -195,7 +195,7 @@ class MessageHandler(
val timestamp = currentTime.plus(time.toLong(), ChronoUnit.MINUTES) val timestamp = currentTime.plus(time.toLong(), ChronoUnit.MINUTES)
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
dispatcher.post(TimerEvent(user.token!!, TimerType.TIMER, timestamp.toString())) dispatcher.post(TimerEvent(user.token, TimerType.TIMER, timestamp.toString()))
} }
} catch (e: NumberFormatException) { } catch (e: NumberFormatException) {
listener.sendChat("!타이머/숫자 형식으로 적어주세요! 단위: 분") listener.sendChat("!타이머/숫자 형식으로 적어주세요! 단위: 분")
@ -267,7 +267,7 @@ class MessageHandler(
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
dispatcher.post( dispatcher.post(
SongEvent( SongEvent(
user.token!!, user.token,
SongType.ADD, SongType.ADD,
msg.userId, msg.userId,
null, null,

View File

@ -8,9 +8,8 @@ import org.jetbrains.exposed.dao.id.IntIdTable
object Users: IntIdTable("users") { object Users: IntIdTable("users") {
val username = varchar("username", 255) val username = varchar("username", 255)
val token = varchar("token", 64).nullable() val token = varchar("token", 64)
val discord = long("discord").nullable() val discord = long("discord").nullable()
val naverId = varchar("naver_id", 128)
val liveAlertGuild = long("live_alert_guild").nullable() val liveAlertGuild = long("live_alert_guild").nullable()
val liveAlertChannel = long("live_alert_channel").nullable() val liveAlertChannel = long("live_alert_channel").nullable()
val liveAlertMessage = text("live_alert_message").nullable() val liveAlertMessage = text("live_alert_message").nullable()
@ -24,7 +23,6 @@ class User(id: EntityID<Int>) : IntEntity(id) {
var username by Users.username var username by Users.username
var token by Users.token var token by Users.token
var discord by Users.discord var discord by Users.discord
var naverId by Users.naverId
var liveAlertGuild by Users.liveAlertGuild var liveAlertGuild by Users.liveAlertGuild
var liveAlertChannel by Users.liveAlertChannel var liveAlertChannel by Users.liveAlertChannel
var liveAlertMessage by Users.liveAlertMessage var liveAlertMessage by Users.liveAlertMessage

View File

@ -6,11 +6,11 @@ import space.mori.chzzk_bot.common.models.User
import space.mori.chzzk_bot.common.models.Users import space.mori.chzzk_bot.common.models.Users
object UserService { object UserService {
fun saveUser(username: String, naverId: String): User { fun saveUser(username: String, token: String): User {
return transaction { return transaction {
User.new { User.new {
this.username = username this.username = username
this.naverId = naverId this.token = token
} }
} }
} }
@ -64,14 +64,6 @@ object UserService {
} }
} }
fun getUserWithNaverId(naverId: String): User? {
return transaction {
val user = User.find{ Users.naverId eq naverId }.firstOrNull()
user?.load(User::subordinates, User::managers)
user
}
}
fun getAllUsers(): List<User> { fun getAllUsers(): List<User> {
return transaction { return transaction {
User.all().toList() User.all().toList()

View File

@ -26,6 +26,8 @@ import space.mori.chzzk_bot.common.services.UserService
import space.mori.chzzk_bot.webserver.routes.* import space.mori.chzzk_bot.webserver.routes.*
import space.mori.chzzk_bot.webserver.utils.DiscordRatelimits import space.mori.chzzk_bot.webserver.utils.DiscordRatelimits
import wsSongListRoutes import wsSongListRoutes
import java.math.BigInteger
import java.security.SecureRandom
import java.time.Duration import java.time.Duration
import kotlin.time.toKotlinDuration import kotlin.time.toKotlinDuration
@ -33,14 +35,12 @@ val dotenv = dotenv {
ignoreIfMissing = true ignoreIfMissing = true
} }
const val naverMeAPIURL = "https://openapi.naver.com/v1/nid/me"
val redirects = mutableMapOf<String, String>() val redirects = mutableMapOf<String, String>()
val server = embeddedServer(Netty, port = 8080, ) { val server = embeddedServer(Netty, port = 8080, ) {
install(WebSockets) { install(WebSockets) {
pingPeriod = Duration.ofSeconds(15).toKotlinDuration() pingPeriod = Duration.ofSeconds(15).toKotlinDuration()
timeout = Duration.ofSeconds(15).toKotlinDuration() timeout = Duration.ofSeconds(100).toKotlinDuration()
maxFrameSize = Long.MAX_VALUE maxFrameSize = Long.MAX_VALUE
masking = false masking = false
contentConverter = KotlinxWebsocketSerializationConverter(Json) contentConverter = KotlinxWebsocketSerializationConverter(Json)
@ -56,26 +56,6 @@ val server = embeddedServer(Netty, port = 8080, ) {
cookie<UserSession>("user_session", storage = MariadbSessionStorage()) {} cookie<UserSession>("user_session", storage = MariadbSessionStorage()) {}
} }
install(Authentication) { install(Authentication) {
oauth("auth-oauth-naver") {
urlProvider = { "${dotenv["HOST"]}/auth/callback" }
providerLookup = { OAuthServerSettings.OAuth2ServerSettings(
name = "naver",
authorizeUrl = "https://nid.naver.com/oauth2.0/authorize",
accessTokenUrl = "https://nid.naver.com/oauth2.0/token",
requestMethod = HttpMethod.Post,
clientId = dotenv["NAVER_CLIENT_ID"],
clientSecret = dotenv["NAVER_CLIENT_SECRET"],
defaultScopes = listOf(""),
extraAuthParameters = listOf(),
onStateCreated = { call, state ->
//saves new state with redirect url value
call.request.queryParameters["redirectUrl"]?.let {
redirects[state] = it
}
}
)}
client = applicationHttpClient
}
oauth("auth-oauth-discord") { oauth("auth-oauth-discord") {
urlProvider = { "${dotenv["HOST"]}/auth/callback/discord" } urlProvider = { "${dotenv["HOST"]}/auth/callback/discord" }
providerLookup = { OAuthServerSettings.OAuth2ServerSettings( providerLookup = { OAuthServerSettings.OAuth2ServerSettings(
@ -112,7 +92,7 @@ val server = embeddedServer(Netty, port = 8080, ) {
try { try {
val principal = call.principal<OAuthAccessTokenResponse.OAuth2>() val principal = call.principal<OAuthAccessTokenResponse.OAuth2>()
val session = call.sessions.get<UserSession>() val session = call.sessions.get<UserSession>()
val user = session?.id?.let { UserService.getUserWithNaverId(it) } val user = session?.id?.let { UserService.getUser(it) }
if(principal != null && session != null && user != null) { if(principal != null && session != null && user != null) {
try { try {
@ -150,32 +130,81 @@ val server = embeddedServer(Netty, port = 8080, ) {
} }
// naver login // naver login
authenticate("auth-oauth-naver") {
get("/login") { get("/login") {
val state = generateSecureRandomState()
// 세션에 상태 값 저장
call.sessions.set(UserSession(
state,
"",
listOf(),
))
// OAuth 제공자의 인증 URL 구성
val authUrl = URLBuilder("https://chzzk.naver.com/account-interlock").apply {
parameters.append("clientId", dotenv["NAVER_CLIENT_ID"]) // 비표준 파라미터 이름
parameters.append("redirectUri", "${dotenv["HOST"]}/auth/callback")
parameters.append("state", state)
// 추가적인 파라미터가 필요하면 여기에 추가
}.build().toString()
// 사용자에게 인증 페이지로 리다이렉트
call.respondRedirect(authUrl)
} }
get("/callback") { get("/callback") {
val currentPrincipal = call.principal<OAuthAccessTokenResponse.OAuth2>() val receivedState = call.parameters["state"]
currentPrincipal?.let { principal -> val code = call.parameters["code"]
principal.state?.let { state ->
val userInfo: NaverAPI<NaverMeAPI> = applicationHttpClient.get(naverMeAPIURL) {
headers {
append(HttpHeaders.Authorization, "Bearer ${principal.accessToken}")
}
}.body()
call.sessions.set(userInfo.response?.let { profile -> // 세션에서 상태 값 가져오기
UserSession(state, profile.id, listOf()) val session = call.sessions.get<UserSession>()
}) if (session == null || session.state != receivedState) {
call.respond(HttpStatusCode.BadRequest, "Invalid state parameter")
redirects[state]?.let { redirect ->
call.respondRedirect(redirect)
return@get return@get
} }
if (code == null) {
call.respond(HttpStatusCode.BadRequest, "Missing code parameter")
return@get
} }
try {
// Access Token 요청
val tokenRequest = TokenRequest(
grantType = "authorization_code",
state = session.state,
code = code,
clientId = dotenv["NAVER_CLIENT_ID"],
clientSecret = dotenv["NAVER_CLIENT_SECRET"]
)
val response = applicationHttpClient.post("https://chzzk.naver.com/auth/v1/token") {
contentType(ContentType.Application.Json)
setBody(tokenRequest)
} }
val tokenResponse = response.body<TokenResponse>()
if(tokenResponse.content == null) {
call.respond(HttpStatusCode.InternalServerError, "Failed to obtain access token")
return@get
}
// Access Token 사용: 예를 들어, 사용자 정보 요청
val userInfo = getChzzkUser(tokenResponse.content.accessToken)
if(userInfo.content != null) {
call.sessions.set(
UserSession(
session.state,
userInfo.content.channelId,
listOf()
)
)
call.respondRedirect(getFrontendURL("")) call.respondRedirect(getFrontendURL(""))
} }
} catch (e: Exception) {
e.printStackTrace()
call.respond(HttpStatusCode.InternalServerError, "Failed to obtain access token")
}
} }
// common: logout // common: logout
@ -231,16 +260,33 @@ fun getFrontendURL(path: String)
data class UserSession( data class UserSession(
val state: String, val state: String,
val id: String, val id: String,
val discordGuildList: List<String> val discordGuildList: List<String>,
) )
@Serializable @Serializable
data class NaverMeAPI( data class TokenRequest(
val id: String val grantType: String,
val state: String,
val code: String,
val clientId: String,
val clientSecret: String
) )
@Serializable @Serializable
data class NaverAPI<T>(val resultcode: String, val message: String, val response: T?) data class TokenResponse(
val code: Int,
val message: String?,
val content: TokenResponseBody?
)
@Serializable
data class TokenResponseBody(
val accessToken: String,
val tokenType: String,
val expiresIn: Int,
val refreshToken: String? = null
)
@Serializable @Serializable
data class DiscordMeAPI( data class DiscordMeAPI(
@ -353,3 +399,31 @@ suspend fun getUserGuilds(accessToken: String): List<DiscordGuildListAPI> {
return response.body<List<DiscordGuildListAPI>>() return response.body<List<DiscordGuildListAPI>>()
} }
@Serializable
data class ChzzkMeApi(
val channelId: String,
val channelName: String,
val nickname: String,
)
@Serializable
data class ChzzkApi<T>(
val code: Int,
val message: String?,
val content: T?
)
suspend fun getChzzkUser(accessToken: String): ChzzkApi<ChzzkMeApi> {
val response = applicationHttpClient.get("https://openapi.chzzk.naver.com/open/v1/users/me") {
headers {
append(HttpHeaders.Authorization, "Bearer $accessToken")
}
}
return response.body<ChzzkApi<ChzzkMeApi>>()
}
fun generateSecureRandomState(): String {
return BigInteger(130, SecureRandom()).toString(32)
}

View File

@ -1,7 +1,6 @@
package space.mori.chzzk_bot.webserver.routes package space.mori.chzzk_bot.webserver.routes
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.* import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -58,7 +57,7 @@ fun Routing.apiCommandRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@put return@put
} }
@ -70,7 +69,7 @@ fun Routing.apiCommandRoutes() {
) )
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
for(i: Int in 0..3) { for(i: Int in 0..3) {
dispatcher.post(CommandReloadEvent(user.token ?: "")) dispatcher.post(CommandReloadEvent(user.token))
} }
} }
call.respond(HttpStatusCode.OK) call.respond(HttpStatusCode.OK)
@ -93,7 +92,7 @@ fun Routing.apiCommandRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@post return@post
} }
@ -107,7 +106,7 @@ fun Routing.apiCommandRoutes() {
) )
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
for(i: Int in 0..3) { for(i: Int in 0..3) {
dispatcher.post(CommandReloadEvent(user.token ?: "")) dispatcher.post(CommandReloadEvent(user.token))
} }
} }
call.respond(HttpStatusCode.OK) call.respond(HttpStatusCode.OK)
@ -133,7 +132,7 @@ fun Routing.apiCommandRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@delete return@delete
} }
@ -142,7 +141,7 @@ fun Routing.apiCommandRoutes() {
CommandService.removeCommand(user, commandRequest.label) CommandService.removeCommand(user, commandRequest.label)
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
for(i: Int in 0..3) { for(i: Int in 0..3) {
dispatcher.post(CommandReloadEvent(user.token ?: "")) dispatcher.post(CommandReloadEvent(user.token))
} }
} }
call.respond(HttpStatusCode.OK) call.respond(HttpStatusCode.OK)

View File

@ -1,7 +1,6 @@
package space.mori.chzzk_bot.webserver.routes package space.mori.chzzk_bot.webserver.routes
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.* import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -34,7 +33,7 @@ fun Route.apiDiscordRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@get return@get
} }
@ -67,7 +66,7 @@ fun Route.apiDiscordRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@post return@post
} }
@ -86,7 +85,7 @@ fun Route.apiDiscordRoutes() {
call.respond(HttpStatusCode.BadRequest, "Session is required") call.respond(HttpStatusCode.BadRequest, "Session is required")
return@get return@get
} }
val user = UserService.getUserWithNaverId(session.id) val user = UserService.getUser(session.id)
if(user == null) { if(user == null) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@get return@get

View File

@ -96,20 +96,20 @@ fun Routing.apiRoutes() {
call.respondText("No session found", status = HttpStatusCode.Unauthorized) call.respondText("No session found", status = HttpStatusCode.Unauthorized)
return@get return@get
} }
var user = UserService.getUserWithNaverId(session.id) var user = UserService.getUser(session.id)
if(user == null) { if(user == null) {
user = UserService.saveUser("임시닉네임", session.id) user = UserService.saveUser("임시닉네임", session.id)
} }
val songConfig = SongConfigService.getConfig(user) val songConfig = SongConfigService.getConfig(user)
val status = user.token?.let { it1 -> getStreamInfo(it1) } val status = getStreamInfo(user.token)
val returnUsers = mutableListOf<GetSessionDTO>() val returnUsers = mutableListOf<GetSessionDTO>()
if (user.username == "임시닉네임") { if (user.username == "임시닉네임") {
status?.content?.channel?.let { it1 -> UserService.updateUser(user, it1.channelId, it1.channelName) } status.content?.channel?.let { it1 -> UserService.updateUser(user, it1.channelId, it1.channelName) }
} }
if(status?.content == null) { if(status.content == null) {
call.respondText(user.naverId, status = HttpStatusCode.NotFound) call.respondText(user.token, status = HttpStatusCode.NotFound)
return@get return@get
} }
@ -128,8 +128,8 @@ fun Routing.apiRoutes() {
user.subordinates.toList() user.subordinates.toList()
} }
returnUsers.addAll(subordinates.map { returnUsers.addAll(subordinates.map {
val subStatus = it.token?.let { token -> ChzzkUserCache.getCachedUser(token) } val subStatus = it.token.let { token -> ChzzkUserCache.getCachedUser(token) }
return@map if (it.token == null || subStatus?.content == null) { return@map if (subStatus?.content == null) {
null null
} else { } else {
GetSessionDTO( GetSessionDTO(
@ -156,7 +156,7 @@ fun Routing.apiRoutes() {
val body: RegisterChzzkUserDTO = call.receive() val body: RegisterChzzkUserDTO = call.receive()
val user = UserService.getUserWithNaverId(session.id) val user = UserService.getUser(session.id)
if(user == null) { if(user == null) {
call.respondText("No session found", status = HttpStatusCode.Unauthorized) call.respondText("No session found", status = HttpStatusCode.Unauthorized)
return@post return@post
@ -197,7 +197,7 @@ fun Routing.apiRoutes() {
return@get return@get
} }
val user = UserService.getUserWithNaverId(session.id) val user = UserService.getUser(session.id)
if(user == null) { if(user == null) {
call.respondText("No session found", status = HttpStatusCode.Unauthorized) call.respondText("No session found", status = HttpStatusCode.Unauthorized)
return@get return@get
@ -216,7 +216,7 @@ fun Routing.apiRoutes() {
val body: GetSettingDTO = call.receive() val body: GetSettingDTO = call.receive()
val user = UserService.getUserWithNaverId(session.id) val user = UserService.getUser(session.id)
if(user == null) { if(user == null) {
call.respondText("No session found", status = HttpStatusCode.Unauthorized) call.respondText("No session found", status = HttpStatusCode.Unauthorized)
return@post return@post

View File

@ -1,7 +1,6 @@
package space.mori.chzzk_bot.webserver.routes package space.mori.chzzk_bot.webserver.routes
import io.ktor.http.* import io.ktor.http.*
import io.ktor.server.application.*
import io.ktor.server.request.* import io.ktor.server.request.*
import io.ktor.server.response.* import io.ktor.server.response.*
import io.ktor.server.routing.* import io.ktor.server.routing.*
@ -31,7 +30,7 @@ fun Routing.apiTimerRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@get return@get
} }
@ -58,7 +57,7 @@ fun Routing.apiTimerRoutes() {
val managers = transaction { val managers = transaction {
user.managers.toList() user.managers.toList()
} }
if(!managers.any { it.naverId == session?.id } && user.naverId != session?.id) { if(!managers.any { it.token == session?.id } && user.token != session?.id) {
call.respond(HttpStatusCode.BadRequest, "User does not exist") call.respond(HttpStatusCode.BadRequest, "User does not exist")
return@put return@put
} }

View File

@ -109,7 +109,7 @@ fun Routing.wsSongListRoutes() {
webSocket("/songlist") { webSocket("/songlist") {
val session = call.sessions.get<UserSession>() val session = call.sessions.get<UserSession>()
val user = session?.id?.let { UserService.getUserWithNaverId(it) } val user = session?.id?.let { UserService.getUser(it) }
if (user == null) { if (user == null) {
close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Invalid SID")) close(CloseReason(CloseReason.Codes.CANNOT_ACCEPT, "Invalid SID"))
return@webSocket return@webSocket
@ -117,7 +117,7 @@ fun Routing.wsSongListRoutes() {
val uid = user.token val uid = user.token
addSession(uid!!, this) addSession(uid, this)
if (status[uid] == SongType.STREAM_OFF) { if (status[uid] == SongType.STREAM_OFF) {
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
@ -161,9 +161,8 @@ fun Routing.wsSongListRoutes() {
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
val user = UserService.getUser(it.uid) val user = UserService.getUser(it.uid)
if (user != null) { if (user != null) {
user.token?.let { token ->
sendWithRetry( sendWithRetry(
token, SongResponse( user.token, SongResponse(
it.type.value, it.type.value,
it.uid, it.uid,
it.reqUid, it.reqUid,
@ -175,16 +174,14 @@ fun Routing.wsSongListRoutes() {
} }
} }
} }
}
dispatcher.subscribe(TimerEvent::class) { dispatcher.subscribe(TimerEvent::class) {
if (it.type == TimerType.STREAM_OFF) { if (it.type == TimerType.STREAM_OFF) {
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
val user = UserService.getUser(it.uid) val user = UserService.getUser(it.uid)
if (user != null) { if (user != null) {
user.token?.let { token ->
sendWithRetry( sendWithRetry(
token, SongResponse( user.token, SongResponse(
it.type.value, it.type.value,
it.uid, it.uid,
null, null,
@ -196,7 +193,6 @@ fun Routing.wsSongListRoutes() {
} }
} }
} }
}
} }
suspend fun handleSongRequest( suspend fun handleSongRequest(
@ -219,7 +215,7 @@ suspend fun handleSongRequest(
CoroutineScope(Dispatchers.Default).launch { CoroutineScope(Dispatchers.Default).launch {
SongListService.saveSong( SongListService.saveSong(
user, user,
user.token!!, user.token,
url, url,
youtubeVideo.name, youtubeVideo.name,
youtubeVideo.author, youtubeVideo.author,
@ -228,7 +224,7 @@ suspend fun handleSongRequest(
) )
dispatcher.post( dispatcher.post(
SongEvent( SongEvent(
user.token!!, user.token,
SongType.ADD, SongType.ADD,
user.token, user.token,
CurrentSong.getSong(user), CurrentSong.getSong(user),
@ -251,7 +247,7 @@ suspend fun handleSongRequest(
} }
dispatcher.post( dispatcher.post(
SongEvent( SongEvent(
user.token!!, user.token,
SongType.REMOVE, SongType.REMOVE,
null, null,
null, null,
@ -281,7 +277,7 @@ suspend fun handleSongRequest(
} }
dispatcher.post( dispatcher.post(
SongEvent( SongEvent(
user.token!!, user.token,
SongType.NEXT, SongType.NEXT,
song?.uid, song?.uid,
youtubeVideo youtubeVideo