Basic complete: packet parse, zlib decompress

This commit is contained in:
2026-08-30 19:30:00 +08:00
parent 7e177214a9
commit 9ce6fc32a0
24 files changed
+410 -63

No files matched your search

@@ -0,0 +1,91 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
@file:OptIn(InternalBldmApi::class)
package cn.rtast.bldm.client
import cn.rtast.bldm.core.BLDMConstants
import cn.rtast.bldm.core.annotations.InternalBldmApi
import cn.rtast.bldm.core.data.DMServerConf
import cn.rtast.bldm.core.data.RealRoomId
import cn.rtast.bldm.core.data.UserNavData
import cn.rtast.bldm.core.protocol.Packet
import cn.rtast.bldm.core.protocol.PacketDecoder
import cn.rtast.bldm.core.protocol.PacketEncoder
import cn.rtast.bldm.core.protocol.event.PacketEvents
import cn.rtast.bldm.core.util._fromJson
import cn.rtast.bldm.core.util.parseBuvidAndUid
import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.websocket.*
import kotlinx.coroutines.delay
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlin.time.Duration.Companion.seconds
private val HEARTBEAT_INTERVAL = 30.seconds
public suspend fun connectToBlDM(
roomId: Long,
cookie: String?,
events: PacketEvents.() -> Unit,
) {
val parsedCookie = parseBuvidAndUid(cookie ?: "")
val packetEncoder = PacketEncoder()
val packetDecoder = PacketDecoder(events)
val realRoomId = httpClient.get(BLDMConstants.REAL_ROOM_ID_URL + roomId)
.bodyAsText()._fromJson<RealRoomId>()
val wbi = httpClient.get(BLDMConstants.USER_NAV_URL)
.bodyAsText()._fromJson<UserNavData>().signWbi(realRoomId.data.roomId)
val serverConf = httpClient.get(BLDMConstants.DM_SERVER_CONF_URL + "?$wbi") {
cookie?.let { header(HttpHeaders.Cookie, it) }
}.bodyAsText()._fromJson<DMServerConf>().data
val wsUrl = "ws://${serverConf.hostList.first().host}:${serverConf.hostList.first().wsPort}/sub"
httpClient.webSocket(wsUrl, {
// header(HttpHeaders.Host, "${serverConf.hostList.first().host}:${serverConf.hostList.first().wssPort}")
// header(HttpHeaders.Origin, "https://www.bilibili.com")
}) {
val authPacket = packetEncoder.authPacket(
parsedCookie.second ?: 0,
realRoomId.data.roomId,
parsedCookie.first ?: "",
serverConf.token
)
sendPacket(authPacket)
val heartbeatJob = launch {
val heartbeatPacket = packetEncoder.heartbeatPacket()
while (isActive) {
delay(HEARTBEAT_INTERVAL)
runCatching {
sendPacket(heartbeatPacket)
}.onFailure {
println("heartbeat failed ${it.message}")
}
}
}
try {
for (frame in incoming) {
if (frame !is Frame.Binary) continue
val data = frame.readBytes()
packetDecoder.decode(data)
}
} catch (e: Exception) {
e.printStackTrace()
} finally {
heartbeatJob.cancel()
println("Closed")
}
}
httpClient.close()
}
internal suspend fun DefaultWebSocketSession.sendPacket(packet: Packet) =
send(Frame.Binary(true, packet.toByteArray()))
@@ -0,0 +1,25 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.client
import io.ktor.client.*
import io.ktor.client.plugins.defaultRequest
import io.ktor.client.plugins.websocket.*
import io.ktor.client.request.header
import io.ktor.http.HttpHeaders
internal val httpClient = HttpClient {
install(WebSockets)
defaultRequest {
header(HttpHeaders.UserAgent, USER_AGENT)
}
}
internal const val USER_AGENT =
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/151.0.0.0 Safari/537.36"
@@ -0,0 +1,39 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package test
import cn.rtast.bldm.client.connectToBlDM
import kotlinx.coroutines.test.runTest
import kotlinx.io.buffered
import kotlinx.io.files.Path
import kotlinx.io.files.SystemFileSystem
import kotlinx.io.readString
import kotlin.test.Test
class TestClient {
private val cookie = SystemFileSystem.source(Path("src/commonTest/resources/cookie.txt"))
.buffered().readString()
@Test
fun `test bldm client`() = runTest {
connectToBlDM(6, cookie) {
onHeartbeat {
println("Heartbeat")
}
onMessage {
println(it.content)
}
onAuthReply {
println("auth reply")
}
}
}
}
@@ -0,0 +1 @@
Rename this file to cookie.txt and paste cookie here