Rename module name and add dm-codec module to serialization/deserialization dm message

This commit is contained in:
2026-08-30 22:09:27 +08:00
parent 9ce6fc32a0
commit 539a300946
42 files changed
+886 -55

No files matched your search

+3
View File
@@ -0,0 +1,3 @@
dm codec
WIP
+28
View File
@@ -0,0 +1,28 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
plugins {
kotlin("multiplatform")
kotlin("plugin.serialization")
}
kotlin {
explicitApi()
linuxX64()
linuxArm64()
macosArm64()
mingwX64()
jvm { compilerOptions.jvmTarget = JvmTarget.JVM_11 }
sourceSets {
commonMain.dependencies {
api(project(":bl-codec"))
api("org.jetbrains.kotlinx:kotlinx-serialization-protobuf:1.11.0")
}
commonTest.dependencies {
implementation(kotlin("test"))
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.11.0")
}
}
}
@@ -0,0 +1,43 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.Serializable
@Serializable
public data class InteractWordV2(
override val origin: String,
@ReverseEnginedProtobuf override val reversed: InteractWordPbPayload,
val dmScore: Int,
) : _ReversedProtobufClass<InteractWordV2.InteractWordPbPayload> {
@Serializable
public data class InteractWordPbPayload(
val uid: Long,
val unameBytes: Map<String, Int>,
val actionType: Int,
val roomId: Long,
val timestampSec: Long,
val timestampMs: Long,
val identityType: Int,
val unameColor: String,
val sourceBytes: Map<String, Int>,
val msgId: ULong,
val userInfo: UserInfo,
)
@Serializable
public data class UserInfo(
val uid: Long,
val userDetail: UserDetail,
)
@Serializable
public data class UserDetail(
val faceUrl: String,
)
}
@@ -0,0 +1,18 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.Serializable
@Serializable
public data class OnlineRankCount(
val count: Int,
val countText: String,
val onlineCount: Int,
val onlineCountText: String,
)
@@ -0,0 +1,70 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.Serializable
/**
* this data class defined
*/
@Serializable
public data class OnlineRankV3(
override val origin: String,
@ReverseEnginedProtobuf override val reversed: ReversedProtobuf,
) : _ReversedProtobufClass<OnlineRankV3.ReversedProtobuf> {
@Serializable
public data class ReversedProtobuf(
/**
* rank type
* `online_rank`
*/
val rankType: String,
val userRankInfo: List<UserRankInfo>,
)
@Serializable
public data class UserRankInfo(
val uid: Long,
val avatarUrl: String,
val scoreInfo: UserScore,
val nickname: String,
val rank: Int,
val accountDetail: AccountDetail,
)
@Serializable
public data class UserScore(
val score: Int,
)
@Serializable
public data class AccountDetail(
val uid: Long,
val profile: ProfileCard,
)
@Serializable
public data class ProfileCard(
val nickname: String,
val faceUrl: String,
val avatarMedia: MediaInfo,
val iconMedia: MediaInfo,
val extraFlags: ExtraFlags,
)
@Serializable
public data class MediaInfo(
val nickname: String,
val avatarUrl: String,
)
@Serializable
public data class ExtraFlags(
val flagValue: Long,
)
}
@@ -0,0 +1,282 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
public sealed class RawDanmuMessage {
@Serializable
@SerialName("STOP_LIVE_ROOM_LIST")
public data class RawStopLiveRoomListMsg(val data: Data) : RawDanmuMessage() {
@Serializable
public data class Data(
@SerialName("room_id_list")
val roomIdList: List<Long>,
)
}
@Serializable
@SerialName("INTERACT_WORD_V2")
public data class RawInteractWordV2(val data: Data) : RawDanmuMessage() {
@Serializable
public data class Data(
@SerialName("dmscore")
val dmScore: Int,
/**
* base64 encoded protobuf content
*/
val pb: String,
)
}
@Serializable
@SerialName("LIKE_INFO_V3_CLICK")
public data class RawLikeInfoV3ClickMsg(val data: Data) : RawDanmuMessage() {
@Serializable
public data class Data(
@SerialName("contribution_info")
val contributionInfo: ContributionInfo,
val dmscore: Int,
@SerialName("fans_medal")
val fansMedal: FansMedal,
@SerialName("group_medal")
val groupMedal: String?,
val identities: List<Int>,
@SerialName("is_mystery")
val isMystery: Boolean,
@SerialName("like_icon")
val likeIcon: String,
@SerialName("like_text")
val likeText: String,
@SerialName("msg_type")
val msgType: Int,
@SerialName("show_area")
val showArea: Int,
val uid: Long,
val uinfo: UInfo,
val uname: String,
@SerialName("uname_color")
val unameColor: String,
)
@Serializable
public data class ContributionInfo(
val grade: Int,
)
@Serializable
public data class FansMedal(
@SerialName("anchor_roomid")
val anchorRoomid: Long,
@SerialName("guard_level")
val guardLevel: Int,
@SerialName("icon_id")
val iconId: Int,
@SerialName("is_lighted")
val isLighted: Int,
@SerialName("medal_color")
val medalColor: Long,
@SerialName("medal_color_border")
val medalColorBorder: Long,
@SerialName("medal_color_end")
val medalColorEnd: Long,
@SerialName("medal_color_start")
val medalColorStart: Long,
@SerialName("medal_level")
val medalLevel: Int,
@SerialName("medal_name")
val medalName: String,
val score: Long,
val special: String,
@SerialName("target_id")
val targetId: Long,
)
@Serializable
public data class UInfo(
val anon: String?,
val base: BaseInfo?,
val guard: String?,
@SerialName("guard_leader")
val guardLeader: String?,
val medal: String?,
val title: String?,
@SerialName("uhead_frame")
val uheadFrame: String?,
val uid: Long,
val wealth: String?,
)
@Serializable
public data class BaseInfo(
val face: String,
@SerialName("is_mystery")
val isMystery: Boolean,
val name: String,
@SerialName("name_color")
val nameColor: Int,
@SerialName("name_color_str")
val nameColorStr: String,
@SerialName("official_info")
val officialInfo: OfficialInfo,
@SerialName("origin_info")
val originInfo: OriginInfo,
@SerialName("risk_ctrl_info")
val riskCtrlInfo: String?,
)
@Serializable
public data class OfficialInfo(
val desc: String,
val role: Int,
val title: String,
val type: Int,
)
@Serializable
public data class OriginInfo(
val face: String,
val name: String,
)
}
@Serializable
@SerialName("RECALL_DANMU_MSG")
public data class RawRecallDanmuMsg(
val data: Data,
) : RawDanmuMessage() {
@Serializable
public data class Data(
@SerialName("recall_type")
val recallType: Int,
@SerialName("target_id")
val targetId: Long,
val uinfo: UInfo,
)
@Serializable
public data class UInfo(
val uid: Long,
val base: String?,
val medal: String?,
val wealth: String?,
val title: String?,
val guard: String?,
@SerialName("uhead_frame")
val uheadFrame: String?,
@SerialName("guard_leader")
val guardLeader: String?,
val anon: String?,
@SerialName("bubble_box")
val bubbleBox: String?,
@SerialName("dm_config")
val dmConfig: String?,
@SerialName("name_color")
val nameColor: String?,
)
}
@Serializable
@SerialName("ONLINE_RANK_COUNT")
public data class RawOnlineRankCount(
val data: Data,
) : RawDanmuMessage() {
@Serializable
public data class Data(
val count: Int,
@SerialName("count_text")
val countText: String,
@SerialName("online_count")
val onlineCount: Int,
@SerialName("online_count_text")
val onlineCountText: String,
)
}
@Serializable
@SerialName("SEND_GIFT_V2")
public data class RawSendGiftV2(
val danmu: Danmu,
val data: Data,
) : RawDanmuMessage() {
@Serializable
public data class Danmu(
/**
* live room area id
*/
val area: Int,
)
@Serializable
public data class Data(
/**
* maybe means `高能积分`
* depends on gift values
*/
@SerialName("dmscore")
val dmScore: Int,
/**
* base64 encoded protobuf content
*/
val pb: String,
)
}
@Serializable
@SerialName("LIKE_INFO_V3_UPDATE")
public data class RawLikeInfoV3Update(val data: Data) : RawDanmuMessage() {
@Serializable
public data class Data(
/**
* total like count
*/
@SerialName("click_count")
val clickCount: Int,
)
}
@Serializable
@SerialName("ONLINE_RANK_V3")
public data class RawOnlineRankV3(val data: Data) : RawDanmuMessage() {
@Serializable
public data class Data(
/**
* base64 encoded protobuf content
*/
val pb: String,
)
}
@Serializable
@SerialName("WATCHED_CHANGE")
public data class RawWatchedChange(
val data: Data,
) : RawDanmuMessage() {
@Serializable
public data class Data(
/**
* example `305251371`
*/
val num: Long,
/**
* example `30525.1万`
*/
@SerialName("text_small")
val textSmall: String,
/**
* example `30525.1万人看过`
*/
@SerialName("text_large")
val textLarge: String,
)
}
}
@@ -0,0 +1,16 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
/**
* mark a class or property as derived through protobuf reverse engineering
*/
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY)
@Retention(AnnotationRetention.BINARY)
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
public annotation class ReverseEnginedProtobuf
@@ -0,0 +1,61 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.Serializable
@Serializable
public data class SendGiftV2(
override val origin: String,
@ReverseEnginedProtobuf override val reversed: ReversedProtobuf,
val area: Int,
val dmScore: Int,
) : _ReversedProtobufClass<SendGiftV2.ReversedProtobuf> {
@Serializable
public data class ReversedProtobuf(
val comboId: Long,
val senderNameBytes: Map<String, Int>,
val senderFace: String,
val giftDetail: GiftDetail,
val status: Int,
val senderInfo: SenderInfo,
)
@Serializable
public data class GiftDetail(
val giftId: Int,
val giftNameBytes: Map<String, Int>,
val price: Int,
val coinType: String,
val timestamp: Long,
val comboCount: Int,
val batchComboId: String,
val batchNum: Int,
val totalCoin: Int,
val actionBytes: Map<String, Int>,
val anchorInfo: AnchorInfo,
val giftAssets: GiftAssets,
)
@Serializable
public data class AnchorInfo(
val anchorUid: Long,
)
@Serializable
public data class GiftAssets(
val icon: String,
val webp: String,
val gif: String,
)
@Serializable
public data class SenderInfo(
val comboId: Long,
)
}
@@ -0,0 +1,29 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
public data class WatchedChange(
/**
* number of watched
*/
val num: Long,
/**
* short formatted number
*/
@SerialName("text_small")
val textSmall: String,
/**
* long formatted number
*/
@SerialName("text_large")
val textLarge: String,
)
@@ -0,0 +1,13 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec
@Suppress("ClassName")
public interface _ReversedProtobufClass<T> {
public val origin: String
public val reversed: T
}
@@ -0,0 +1,13 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
package cn.rtast.bldm.dm.codec.util
import kotlin.io.encoding.Base64
internal fun String.decodeBase64ToByteArray(): ByteArray = Base64.decode(this.encodeToByteArray())
internal fun String.decodeBase64ToString(): String = Base64.decode(this.encodeToByteArray()).decodeToString()
@@ -0,0 +1,17 @@
/*
* Copyright © 2026 RTAkland
* Author: RTAkland
* Date: 2026/8/30
*/
@file:OptIn(ExperimentalSerializationApi::class)
package cn.rtast.bldm.dm.codec.util
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.protobuf.ProtoBuf
internal inline fun <reified T> ByteArray.fromProtobuf(): T =
ProtoBuf.decodeFromByteArray(this)