mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-13 06:30:13 +08:00
Utilities for QQAndroid
This commit is contained in:
parent
7bdaa9b174
commit
d61a2df146
@ -76,6 +76,7 @@ kotlin {
|
||||
dependencies {
|
||||
api(kotlin("test-annotations-common"))
|
||||
api(kotlin("test-common"))
|
||||
implementation(kotlin("script-runtime"))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,3 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
actual typealias Context = android.content.Context
|
@ -0,0 +1,83 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Build
|
||||
import android.telephony.TelephonyManager
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.utils.localIpAddress
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* Delegated by [Build]
|
||||
*/
|
||||
actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(context) {
|
||||
override val display: ByteArray get() = Build.DISPLAY.toByteArray()
|
||||
override val product: ByteArray get() = Build.PRODUCT.toByteArray()
|
||||
override val device: ByteArray get() = Build.DEVICE.toByteArray()
|
||||
override val board: ByteArray get() = Build.BOARD.toByteArray()
|
||||
override val brand: ByteArray get() = Build.BRAND.toByteArray()
|
||||
override val model: ByteArray get() = Build.MODEL.toByteArray()
|
||||
override val bootloader: ByteArray get() = Build.BOOTLOADER.toByteArray()
|
||||
|
||||
override val baseBand: ByteArray
|
||||
@SuppressLint("PrivateApi")
|
||||
@Suppress("SpellCheckingInspection")
|
||||
get() = kotlin.runCatching {
|
||||
Class.forName("android.os.SystemProperties").let { clazz ->
|
||||
clazz.getMethod("get", String::class.java, String::class.java)
|
||||
.invoke(clazz.newInstance(), "gsm.version.baseband", "no message")
|
||||
?.toString() ?: ""
|
||||
}
|
||||
}.getOrElse { "" }.toByteArray()
|
||||
|
||||
override val fingerprint: ByteArray get() = Build.FINGERPRINT.toByteArray()
|
||||
override val procVersion: ByteArray
|
||||
get() = File("/proc/version").useLines { it.firstOrNull() ?: "" }.toByteArray()
|
||||
override val bootId: ByteArray
|
||||
get() = File("/proc/sys/kernel/random/boot_id").useLines { it.firstOrNull() ?: "" }.toByteArray()
|
||||
override val version: DeviceInfo.Version get() = Version
|
||||
|
||||
override val simInfo: ByteArray
|
||||
@SuppressLint("WrongConstant")
|
||||
get() {
|
||||
return kotlin.runCatching {
|
||||
val telephonyManager = context.getSystemService("phone") as TelephonyManager
|
||||
if (telephonyManager.simState == 5) {
|
||||
telephonyManager.simOperatorName.toByteArray()
|
||||
} else byteArrayOf()
|
||||
}.getOrElse { byteArrayOf() }
|
||||
}
|
||||
|
||||
override val osType: ByteArray = "android".toByteArray()
|
||||
override val macAddress: ByteArray get() = "02:00:00:00:00:00".toByteArray()
|
||||
override val wifiBSSID: ByteArray?
|
||||
get() = TODO("not implemented")
|
||||
override val wifiSSID: ByteArray?
|
||||
get() = TODO("not implemented")
|
||||
override val imsiMd5: ByteArray // null due to permission READ_PHONE_STATE required
|
||||
get() = md5(byteArrayOf())/*{
|
||||
val telephonyManager = context.getSystemService("phone") as TelephonyManager
|
||||
if (telephonyManager != null) {
|
||||
val subscriberId = telephonyManager.subscriberId
|
||||
if (subscriberId != null) {
|
||||
return subscriberId.toByteArray()
|
||||
}
|
||||
}
|
||||
return kotlin.runCatching {
|
||||
val telephonyManager = context.getSystemService("phone") as TelephonyManager
|
||||
if (telephonyManager != null) {
|
||||
telephonyManager.subscriberId.toByteArray()
|
||||
} else byteArrayOf()
|
||||
}.getOrElse { byteArrayOf() }
|
||||
}*/
|
||||
override val ipAddress: ByteArray get() = localIpAddress().toByteArray()
|
||||
override val androidId: ByteArray get() = Build.ID.toByteArray()
|
||||
override val apn: ByteArray get() = "wifi".toByteArray()
|
||||
|
||||
object Version : DeviceInfo.Version {
|
||||
override val incremental: ByteArray get() = Build.VERSION.INCREMENTAL.toByteArray()
|
||||
override val release: ByteArray get() = Build.VERSION.RELEASE.toByteArray()
|
||||
override val codename: ByteArray get() = Build.VERSION.CODENAME.toByteArray()
|
||||
}
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||
|
||||
/**
|
||||
* From QQAndroid 8.2.0
|
||||
* `oicq.wlogin_sdk.tools.EcdhCrypt`
|
||||
*
|
||||
* Constant to avoid calculations
|
||||
*/
|
||||
interface ECDH {
|
||||
object Default : ECDH {
|
||||
override val publicKey: ByteArray = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes()
|
||||
override val shareKey: ByteArray = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
|
||||
override val privateKey: ByteArray = ByteArray(16)
|
||||
}
|
||||
|
||||
val publicKey: ByteArray
|
||||
|
||||
val shareKey: ByteArray
|
||||
|
||||
val privateKey: ByteArray
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
APP ID:
|
||||
GetStViaSMSVerifyLogin = 16
|
||||
GetStWithoutPasswd = 16
|
||||
*/
|
||||
|
||||
|
||||
class QQAndroidDevice(
|
||||
private val account: BotAccount,
|
||||
/**
|
||||
* 协议版本?, 8.2.0 的为 8001
|
||||
*/
|
||||
@PublishedApi
|
||||
internal val protocolVersion: Short = 8001,
|
||||
|
||||
@PublishedApi
|
||||
internal val ecdh: ECDH = ECDH.Default,
|
||||
|
||||
@PublishedApi
|
||||
internal val appClientVersion: Int
|
||||
) {
|
||||
val uin: Long get() = account.id
|
||||
val password: String get() = account.password
|
||||
|
||||
object Debugging {
|
||||
|
||||
}
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
package net.mamoe.mirai.qqandroid.network
|
||||
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.BotAccount
|
||||
import net.mamoe.mirai.qqandroid.utils.*
|
||||
|
||||
/*
|
||||
APP ID:
|
||||
GetStViaSMSVerifyLogin = 16
|
||||
GetStWithoutPasswd = 16
|
||||
|
||||
|
||||
TICKET ID
|
||||
Pskey = 0x10_0000, from oicq/wlogin_sdk/request/WtloginHelper.java:2980
|
||||
Skey = 0x1000 from oicq/wlogin_sdk/request/WtloginHelper.java:2986
|
||||
|
||||
DOMAINS
|
||||
Pskey: "openmobile.qq.com"
|
||||
*/
|
||||
|
||||
@PublishedApi
|
||||
internal open class QQAndroidClient(
|
||||
val context: Context,
|
||||
val account: BotAccount,
|
||||
|
||||
val ecdh: ECDH = ECDH.Default,
|
||||
val device: DeviceInfo = SystemDeviceInfo(context)
|
||||
) {
|
||||
val tgtgtKey: ByteArray = generateTgtgtKey(device.guid)
|
||||
|
||||
var miscBitMap: Int = 150470524
|
||||
var mainSigMap: Int = 16724722
|
||||
var subSigMap: Int = 0x10400 //=66,560
|
||||
|
||||
var ssoSequenceId: Int = 0
|
||||
var openAppId: Long = 715019303L
|
||||
|
||||
var ipv6NetType: Int = TODO()
|
||||
|
||||
val apkVersionName: ByteArray = "8.2.0".toByteArray()
|
||||
|
||||
val appClientVersion: Int = TODO()
|
||||
|
||||
val apkSignatureMd5: ByteArray = TODO()
|
||||
|
||||
/**
|
||||
* 协议版本?, 8.2.0 的为 8001
|
||||
*/
|
||||
val protocolVersion: Short = 8001
|
||||
|
||||
@Suppress("SpellCheckingInspection")
|
||||
@PublishedApi
|
||||
internal val apkId: ByteArray = "com.tencent.mobileqq".toByteArray()
|
||||
}
|
||||
|
@ -3,10 +3,13 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import net.mamoe.mirai.data.Packet
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidDevice
|
||||
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.PacketId
|
||||
import net.mamoe.mirai.qqandroid.utils.ECDH
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.io.encryptAndWrite
|
||||
import net.mamoe.mirai.utils.io.writeQQ
|
||||
import net.mamoe.mirai.utils.io.writeShortLVByteArray
|
||||
|
||||
/**
|
||||
* 待发送给服务器的数据包. 它代表着一个 [ByteReadPacket],
|
||||
@ -101,8 +104,8 @@ inline class EncryptMethod(val value: UByte) {
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
inline fun PacketFactory<*, *>.buildOutgoingPacket(
|
||||
device: QQAndroidDevice,
|
||||
internal inline fun PacketFactory<*, *>.buildOutgoingPacket(
|
||||
client: QQAndroidClient,
|
||||
encryptMethod: EncryptMethod,
|
||||
name: String? = null,
|
||||
id: PacketId = this.id,
|
||||
@ -114,15 +117,15 @@ inline fun PacketFactory<*, *>.buildOutgoingPacket(
|
||||
// Head
|
||||
writeByte(0x02) // head
|
||||
writeShort((27 + 2 + body.remaining).toShort()) // orthodox algorithm
|
||||
writeShort(device.protocolVersion)
|
||||
writeShort(client.protocolVersion)
|
||||
writeShort(id.commandId.toShort())
|
||||
writeShort(sequenceId.toShort())
|
||||
writeQQ(device.uin)
|
||||
writeQQ(client.account.id)
|
||||
writeByte(3) // originally const
|
||||
writeUByte(encryptMethod.value)
|
||||
writeByte(0) // const8_always_0
|
||||
writeInt(2) // originally const
|
||||
writeInt(device.appClientVersion)
|
||||
writeInt(client.appClientVersion)
|
||||
writeInt(0) // constp_always_0
|
||||
|
||||
// Body
|
||||
@ -131,4 +134,55 @@ inline fun PacketFactory<*, *>.buildOutgoingPacket(
|
||||
// Tail
|
||||
writeByte(0x03) // tail
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* buildPacket{
|
||||
* byte 1
|
||||
* byte 1
|
||||
* fully privateKey
|
||||
* short 258
|
||||
* short publicKey.length
|
||||
* fully publicKey
|
||||
* encryptAndWrite(shareKey, body)
|
||||
* }
|
||||
*/
|
||||
inline fun BytePacketBuilder.writeECDHEncryptedPacket(
|
||||
ecdh: ECDH,
|
||||
body: BytePacketBuilder.() -> Unit
|
||||
) = ecdh.run {
|
||||
writeByte(1) // const
|
||||
writeByte(1) // const
|
||||
writeFully(privateKey)
|
||||
writeShort(258) // const
|
||||
writeShortLVByteArray(publicKey)
|
||||
encryptAndWrite(shareKey, body)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* buildPacket{
|
||||
* byte 1
|
||||
* if loginState == 2:
|
||||
* byte 3
|
||||
* else:
|
||||
* byte 2
|
||||
* fully key
|
||||
* short 258
|
||||
* short 0
|
||||
* fully encrypted
|
||||
* }
|
||||
*/
|
||||
inline fun BytePacketBuilder.writeTEAEncryptedPacket(
|
||||
loginState: Int,
|
||||
key: ByteArray,
|
||||
body: BytePacketBuilder.() -> Unit
|
||||
) {
|
||||
require(loginState == 2 || loginState == 3)
|
||||
writeByte(1) // const
|
||||
writeByte(if (loginState == 2) 3 else 2) // const
|
||||
writeFully(key)
|
||||
writeShort(258) // const
|
||||
writeShort(0) // const, length of publicKey
|
||||
encryptAndWrite(key, body)
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
/**
|
||||
* On Android, typealias to `android.content.Context`
|
||||
* On JVM, empty class.
|
||||
*/
|
||||
expect abstract class Context
|
@ -0,0 +1,79 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import kotlinx.serialization.SerialId
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoBuf
|
||||
|
||||
abstract class DeviceInfo(
|
||||
val context: Context
|
||||
) {
|
||||
abstract val display: ByteArray
|
||||
abstract val product: ByteArray
|
||||
abstract val device: ByteArray
|
||||
abstract val board: ByteArray
|
||||
|
||||
abstract val brand: ByteArray
|
||||
abstract val model: ByteArray
|
||||
abstract val bootloader: ByteArray
|
||||
abstract val fingerprint: ByteArray
|
||||
abstract val bootId: ByteArray
|
||||
|
||||
abstract val procVersion: ByteArray
|
||||
abstract val baseBand: ByteArray
|
||||
|
||||
abstract val version: Version
|
||||
|
||||
abstract val simInfo: ByteArray
|
||||
|
||||
abstract val osType: ByteArray
|
||||
|
||||
abstract val macAddress: ByteArray
|
||||
|
||||
abstract val wifiBSSID: ByteArray?
|
||||
abstract val wifiSSID: ByteArray?
|
||||
|
||||
abstract val imsiMd5: ByteArray
|
||||
|
||||
abstract val ipAddress: ByteArray
|
||||
|
||||
abstract val androidId: ByteArray
|
||||
|
||||
abstract val apn: ByteArray
|
||||
|
||||
val guid: ByteArray get() = generateGuid(androidId, macAddress)
|
||||
|
||||
fun generateDeviceInfoData(): ByteArray {
|
||||
@Serializable
|
||||
class DevInfo(
|
||||
@SerialId(1) val bootloader: ByteArray,
|
||||
@SerialId(2) val procVersion: ByteArray,
|
||||
@SerialId(3) val codename: ByteArray,
|
||||
@SerialId(4) val incremental: ByteArray,
|
||||
@SerialId(5) val fingerprint: ByteArray,
|
||||
@SerialId(6) val bootId: ByteArray,
|
||||
@SerialId(7) val androidId: ByteArray,
|
||||
@SerialId(8) val baseBand: ByteArray,
|
||||
@SerialId(9) val innerVersion: ByteArray
|
||||
)
|
||||
|
||||
return ProtoBuf.dump(
|
||||
DevInfo.serializer(), DevInfo(
|
||||
bootloader,
|
||||
procVersion,
|
||||
version.codename,
|
||||
version.incremental,
|
||||
fingerprint,
|
||||
bootId,
|
||||
androidId,
|
||||
baseBand,
|
||||
version.incremental
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
interface Version {
|
||||
val incremental: ByteArray
|
||||
val release: ByteArray
|
||||
val codename: ByteArray
|
||||
}
|
||||
}
|
@ -0,0 +1,23 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import net.mamoe.mirai.utils.io.chunkedHexToBytes
|
||||
|
||||
/**
|
||||
* From QQAndroid 8.2.0
|
||||
* `oicq.wlogin_sdk.tools.EcdhCrypt`
|
||||
*
|
||||
* Constant to avoid calculations
|
||||
*/
|
||||
interface ECDH {
|
||||
object Default : ECDH {
|
||||
override val publicKey: ByteArray = "020b03cf3d99541f29ffec281bebbd4ea211292ac1f53d7128".chunkedHexToBytes()
|
||||
override val shareKey: ByteArray = "4da0f614fc9f29c2054c77048a6566d7".chunkedHexToBytes()
|
||||
override val privateKey: ByteArray = ByteArray(16)
|
||||
}
|
||||
|
||||
val publicKey: ByteArray
|
||||
|
||||
val shareKey: ByteArray
|
||||
|
||||
val privateKey: ByteArray
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
inline class MacOrAndroidIdChangeFlag(val value: Long = 0) {
|
||||
fun macChanged(): MacOrAndroidIdChangeFlag =
|
||||
MacOrAndroidIdChangeFlag(this.value or 0x1)
|
||||
|
||||
fun androidIdChanged(): MacOrAndroidIdChangeFlag =
|
||||
MacOrAndroidIdChangeFlag(this.value or 0x2)
|
||||
|
||||
fun guidChanged(): MacOrAndroidIdChangeFlag =
|
||||
MacOrAndroidIdChangeFlag(this.value or 0x3)
|
||||
|
||||
companion object {
|
||||
val NoChange: MacOrAndroidIdChangeFlag get() = MacOrAndroidIdChangeFlag()
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import net.mamoe.mirai.utils.md5
|
||||
import kotlin.jvm.JvmStatic
|
||||
|
||||
/**
|
||||
* GUID 来源
|
||||
*
|
||||
* 0: 初始值;
|
||||
* 1: 以前保存的文件;
|
||||
* 20: 以前没保存且现在生成失败;
|
||||
* 17: 以前没保存但现在生成成功;
|
||||
*/
|
||||
@Suppress("NON_PUBLIC_PRIMARY_CONSTRUCTOR_OF_INLINE_CLASS")
|
||||
inline class GuidSource private constructor(val id: Long) { // uint actually
|
||||
companion object {
|
||||
/**
|
||||
* 初始值
|
||||
*/
|
||||
@JvmStatic
|
||||
val STUB = GuidSource(0)
|
||||
/**
|
||||
*
|
||||
*/
|
||||
@JvmStatic
|
||||
val NEWLY_GENERATED = GuidSource(17)
|
||||
/**
|
||||
* 以前没保存但现在生成成功
|
||||
*/
|
||||
@JvmStatic
|
||||
val FROM_STORAGE = GuidSource(1)
|
||||
/**
|
||||
* 以前没保存且现在生成失败
|
||||
*/
|
||||
@JvmStatic
|
||||
val UNAVAILABLE = GuidSource(20)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ```java
|
||||
* GUID_FLAG = 0;
|
||||
* GUID_FLAG |= GUID_SRC << 24 & 0xFF000000;
|
||||
* GUID_FLAG |= FLAG_MAC_ANDROIDID_GUID_CHANGE << 8 & 0xFF00;
|
||||
* ```
|
||||
*
|
||||
* FLAG_MAC_ANDROIDID_GUID_CHANGE:
|
||||
* ```java
|
||||
* if (!Arrays.equals(currentMac, get_last_mac)) {
|
||||
* oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x1;
|
||||
* }
|
||||
* if (!Arrays.equals(currentAndroidId, get_last_android_id)) {
|
||||
* oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x2;
|
||||
* }
|
||||
* if (!Arrays.equals(currentGuid, get_last_guid)) {
|
||||
* oicq.wlogin_sdk.request.t.FLAG_MAC_ANDROIDID_GUID_CHANGEMENT |= 0x4;
|
||||
* }
|
||||
* ```
|
||||
*/
|
||||
internal fun guidFlag(
|
||||
guidSource: GuidSource,
|
||||
macOrAndroidIdChangeFlag: MacOrAndroidIdChangeFlag
|
||||
): Long {
|
||||
var flag = 0L
|
||||
flag = flag or (guidSource.id shl 24 and 0xFF000000)
|
||||
flag = flag or (macOrAndroidIdChangeFlag.value shl 8 and 0xFF00)
|
||||
return flag
|
||||
}
|
||||
|
||||
/**
|
||||
* Defaults "%4;7t>;28<fc.5*6".toByteArray()
|
||||
*/
|
||||
fun generateGuid(androidId: ByteArray, macAddress: ByteArray): ByteArray = md5(androidId + macAddress)
|
@ -0,0 +1,6 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
/**
|
||||
* System default values
|
||||
*/
|
||||
expect class SystemDeviceInfo(context: Context) : DeviceInfo
|
@ -0,0 +1,7 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import net.mamoe.mirai.utils.io.getRandomByteArray
|
||||
import net.mamoe.mirai.utils.md5
|
||||
|
||||
fun generateTgtgtKey(guid: ByteArray): ByteArray =
|
||||
md5(getRandomByteArray(16) + guid)
|
@ -0,0 +1,7 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
/**
|
||||
* Inline the block
|
||||
*/
|
||||
@PublishedApi
|
||||
internal inline fun <R> inline(block: () -> R): R = block()
|
@ -0,0 +1,39 @@
|
||||
package net.mamoe.mirai.qqandroid.utils
|
||||
|
||||
import net.mamoe.mirai.utils.md5
|
||||
|
||||
actual class SystemDeviceInfo actual constructor(context: Context) : DeviceInfo(context) {
|
||||
override val display: ByteArray get() = TODO("not implemented")
|
||||
override val product: ByteArray get() = TODO("not implemented")
|
||||
override val device: ByteArray get() = TODO("not implemented")
|
||||
override val board: ByteArray get() = TODO("not implemented")
|
||||
override val brand: ByteArray get() = TODO("not implemented")
|
||||
override val model: ByteArray get() = TODO("not implemented")
|
||||
override val bootloader: ByteArray get() = TODO("not implemented")
|
||||
override val fingerprint: ByteArray get() = TODO("not implemented")
|
||||
override val bootId: ByteArray get() = TODO("not implemented")
|
||||
override val procVersion: ByteArray get() = "Linux version 3.0.31-g6fb96c9 (android-build@xxx.xxx.xxx.xxx.com)".toByteArray()
|
||||
override val baseBand: ByteArray get() = TODO()
|
||||
override val version: DeviceInfo.Version get() = TODO("not implemented")
|
||||
override val simInfo: ByteArray get() = TODO("not implemented")
|
||||
override val osType: ByteArray get() = TODO("not implemented")
|
||||
override val macAddress: ByteArray get() = TODO("not implemented")
|
||||
override val wifiBSSID: ByteArray?
|
||||
get() = null
|
||||
override val wifiSSID: ByteArray?
|
||||
get() = null
|
||||
override val imsiMd5: ByteArray get() = md5(byteArrayOf())
|
||||
override val ipAddress: ByteArray get() = TODO("not implemented")
|
||||
override val androidId: ByteArray get() = TODO("not implemented")
|
||||
override val apn: ByteArray get() = TODO("not implemented")
|
||||
|
||||
object Version : DeviceInfo.Version {
|
||||
override val incremental: ByteArray get() = TODO("not implemented")
|
||||
override val release: ByteArray get() = TODO("not implemented")
|
||||
override val codename: ByteArray get() = TODO("not implemented")
|
||||
}
|
||||
}
|
||||
|
||||
actual abstract class Context
|
||||
|
||||
open class ContextImpl : Context()
|
@ -0,0 +1,9 @@
|
||||
@file:Suppress("EXPERIMENTAL_API_USAGE")
|
||||
|
||||
package test
|
||||
|
||||
import net.mamoe.mirai.utils.cryptor.protoFieldNumber
|
||||
|
||||
intArrayOf(10, 18, 26, 34, 42, 50, 58, 66, 74).forEach {
|
||||
println(protoFieldNumber(it.toUInt()))
|
||||
}
|
Loading…
Reference in New Issue
Block a user