Commit 23e6ce4e authored by jiahua.liu's avatar jiahua.liu

Merge remote-tracking branch 'origin/master'

# Conflicts:
#	mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/login/Register.kt
parents 24d65666 46864302
package net.mamoe.mirai.qqandroid.io
import kotlinx.io.charsets.Charset
import kotlinx.io.core.*
import kotlin.experimental.or
import kotlin.reflect.KClass
@PublishedApi
internal val CharsetGBK = Charset.forName("GBK")
@PublishedApi
internal val CharsetUTF8 = Charset.forName("UTF8")
inline fun buildJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit): ByteReadPacket {
return JceOutput(stringCharset).apply(block).build()
}
inline fun BytePacketBuilder.writeJcePacket(stringCharset: Charset = CharsetGBK, block: JceOutput.() -> Unit) {
return this.writePacket(buildJcePacket(stringCharset, block))
}
fun jceStruct(tag: Int, struct: JceStruct): ByteArray{
return buildJcePacket {
writeJceStruct(struct, tag)
}.readBytes()
}
fun <K, V> jceMap(tag: Int, vararg entries: Pair<K, V>): ByteArray {
return buildJcePacket {
writeMap(mapOf(*entries), tag)
}.readBytes()
}
/**
*
* From: com.qq.taf.jce.JceOutputStream
*/
@Suppress("unused", "MemberVisibilityCanBePrivate")
@UseExperimental(ExperimentalIoApi::class)
class JceOutput(
private val stringCharset: Charset = CharsetGBK
) {
private val output: BytePacketBuilder = BytePacketBuilder()
fun build(): ByteReadPacket = output.build()
fun close() = output.close()
fun flush() = output.flush()
fun writeByte(v: Byte, tag: Int) {
if (v.toInt() == 0) {
writeHead(ZERO_TYPE, tag)
} else {
writeHead(BYTE, tag)
output.writeByte(v)
}
}
fun writeDouble(v: Double, tag: Int) {
writeHead(DOUBLE, tag)
output.writeDouble(v)
}
fun writeFloat(v: Float, tag: Int) {
writeHead(FLOAT, tag)
output.writeFloat(v)
}
fun writeFully(src: ByteArray, tag: Int) {
writeHead(SIMPLE_LIST, tag)
writeHead(BYTE, 0)
writeInt(src.size, 0)
output.writeFully(src)
}
fun writeFully(src: DoubleArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeDouble(it, 0)
}
}
fun writeFully(src: FloatArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeFloat(it, 0)
}
}
fun writeFully(src: IntArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeInt(it, 0)
}
}
fun writeFully(src: LongArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeLong(it, 0)
}
}
fun writeFully(src: ShortArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeShort(it, 0)
}
}
fun writeFully(src: BooleanArray, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeBoolean(it, 0)
}
}
fun <T> writeFully(src: Array<T>, tag: Int) {
writeHead(LIST, tag)
writeInt(src.size, 0)
src.forEach {
writeObject(it, 0)
}
}
fun writeInt(v: Int, tag: Int) {
if (v in Short.MIN_VALUE..Short.MAX_VALUE) {
writeShort(v.toShort(), tag)
} else {
writeHead(INT, tag)
output.writeInt(v)
}
}
fun writeLong(v: Long, tag: Int) {
if (v in Int.MIN_VALUE..Int.MAX_VALUE) {
writeInt(v.toInt(), tag)
} else {
writeHead(LONG, tag)
output.writeLong(v)
}
}
fun writeShort(v: Short, tag: Int) {
if (v in Byte.MIN_VALUE..Byte.MAX_VALUE) {
writeByte(v.toByte(), tag)
} else {
writeHead(SHORT, tag)
output.writeShort(v)
}
}
fun writeBoolean(v: Boolean, tag: Int) {
this.writeByte(if (v) 1 else 0, tag)
}
fun writeString(v: String, tag: Int) {
val array = v.toByteArray(stringCharset)
if (array.size > 255) {
writeHead(STRING4, tag)
output.writeInt(array.size)
output.writeFully(array)
} else {
writeHead(STRING1, tag)
output.writeByte(array.size.toByte())
output.writeFully(array)
}
}
fun <K, V> writeMap(map: Map<K, V>, tag: Int) {
writeHead(MAP, tag)
if (map.isEmpty()) {
writeInt(0, 0)
} else {
writeInt(map.size, 0)
map.forEach { (key, value) ->
writeObject(key, 0)
writeObject(value, 1)
}
}
}
fun writeCollection(collection: Collection<*>?, tag: Int) {
writeHead(LIST, tag)
if (collection == null || collection.isEmpty()) {
writeInt(0, 0)
} else {
writeInt(collection.size, 0)
collection.forEach {
writeObject(it, 0)
}
}
}
fun writeJceStruct(v: JceStruct, tag: Int) {
writeHead(STRUCT_BEGIN, tag)
v.writeTo(this)
writeHead(STRUCT_END, 0)
}
fun <T> writeObject(v: T, tag: Int) {
when (v) {
is Byte -> writeByte(v, tag)
is Short -> writeShort(v, tag)
is Int -> writeInt(v, tag)
is Long -> writeLong(v, tag)
is Float -> writeFloat(v, tag)
is Double -> writeDouble(v, tag)
is JceStruct -> writeJceStruct(v, tag)
is ByteArray -> writeFully(v, tag)
is Collection<*> -> writeCollection(v, tag)
is Boolean -> writeBoolean(v, tag)
is Map<*, *> -> writeMap(v, tag)
is IntArray -> writeFully(v, tag)
is ShortArray -> writeFully(v, tag)
is BooleanArray -> writeFully(v, tag)
is LongArray -> writeFully(v, tag)
is FloatArray -> writeFully(v, tag)
is DoubleArray -> writeFully(v, tag)
is Array<*> -> writeFully(v, tag)
is String -> writeString(v, tag)
//
// is ByteReadPacket -> ByteArrayPool.useInstance {
// v.readAvailable(it)
// writeFully(it, tag)
// }
else -> error("unsupported type: ${v.getClassName()}")
}
}
fun write(v: Int, tag: Int) = writeInt(v, tag)
fun write(v: Byte, tag: Int) = writeByte(v, tag)
fun write(v: Short, tag: Int) = writeShort(v, tag)
fun write(v: Long, tag: Int) = writeLong(v, tag)
fun write(v: Float, tag: Int) = writeFloat(v, tag)
fun write(v: Double, tag: Int) = writeDouble(v, tag)
fun write(v: String, tag: Int) = writeString(v, tag)
fun write(v: Boolean, tag: Int) = writeBoolean(v, tag)
fun write(v: Collection<*>, tag: Int) = writeCollection(v, tag)
fun write(v: Map<*, *>, tag: Int) = writeMap(v, tag)
fun write(v: ByteArray, tag: Int) = writeFully(v, tag)
fun write(v: IntArray, tag: Int) = writeFully(v, tag)
fun write(v: BooleanArray, tag: Int) = writeFully(v, tag)
fun write(v: LongArray, tag: Int) = writeFully(v, tag)
fun write(v: ShortArray, tag: Int) = writeFully(v, tag)
fun write(v: Array<*>, tag: Int) = writeFully(v, tag)
fun write(v: FloatArray, tag: Int) = writeFully(v, tag)
fun write(v: DoubleArray, tag: Int) = writeFully(v, tag)
@PublishedApi
internal companion object {
const val BYTE: Int = 0
const val DOUBLE: Int = 5
const val FLOAT: Int = 4
const val INT: Int = 2
const val JCE_MAX_STRING_LENGTH = 104857600
const val LIST: Int = 9
const val LONG: Int = 3
const val MAP: Int = 8
const val SHORT: Int = 1
const val SIMPLE_LIST: Int = 13
const val STRING1: Int = 6
const val STRING4: Int = 7
const val STRUCT_BEGIN: Int = 10
const val STRUCT_END: Int = 11
const val ZERO_TYPE: Int = 12
private fun Any?.getClassName(): KClass<out Any> = if (this == null) Unit::class else this::class
}
@PublishedApi
internal fun writeHead(type: Int, tag: Int) {
if (tag < 15) {
this.output.writeByte(((tag shl 4) or type).toByte())
return
}
if (tag < 256) {
this.output.writeByte((type.toByte() or 0xF0.toByte()))
this.output.writeByte(tag.toByte())
return
}
throw JceEncodeException("tag is too large: $tag")
}
}
class JceEncodeException(message: String) : RuntimeException(message)
\ No newline at end of file
package net.mamoe.mirai.qqandroid.io package net.mamoe.mirai.qqandroid.io
interface JceStruct { interface JceStruct
\ No newline at end of file
}
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.jce package net.mamoe.mirai.qqandroid.network.protocol.jce
import kotlinx.serialization.Polymorphic
import kotlinx.serialization.SerialId import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UseSerializers
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
private val EMPTY_MAP = mapOf<String, String>() private val EMPTY_MAP = mapOf<String, String>()
private val EMPTY_SBUFFER_MAP = mapOf<Int, ByteArray>() private val EMPTY_SBUFFER_MAP = mapOf<Int, ByteArray>()
...@@ -15,8 +18,23 @@ class RequestPacket( ...@@ -15,8 +18,23 @@ class RequestPacket(
@SerialId(4) val iRequestId: Int = 0, @SerialId(4) val iRequestId: Int = 0,
@SerialId(5) val sServantName: String = "", @SerialId(5) val sServantName: String = "",
@SerialId(6) val sFuncName: String = "", @SerialId(6) val sFuncName: String = "",
@SerialId(7) val sBuffer: Map<Int, ByteArray> = EMPTY_SBUFFER_MAP, @SerialId(7) val sBuffer: ByteArray = EMPTY_BYTE_ARRAY,
@SerialId(8) val iTimeout: Int = 0, @SerialId(8) val iTimeout: Int = 0,
@SerialId(9) val context: Map<String, String> = EMPTY_MAP, @SerialId(9) val context: Map<String, String> = EMPTY_MAP,
@SerialId(10) val status: Map<String, String> = EMPTY_MAP @SerialId(10) val status: Map<String, String> = EMPTY_MAP
) : JceStruct ) : JceStruct
@Serializable
class RequestDataVersion3(
@SerialId(0) val map: Map<String, ByteArray>
) : JceStruct
@Serializable
class RequestDataVersion2(
@SerialId(0) val map: Map<String, Map<String, ByteArray>>
) : JceStruct
@Serializable
class RequestDataStructSvcReqRegister(
@SerialId(0) val struct: SvcReqRegister
) : JceStruct
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.jce package net.mamoe.mirai.qqandroid.network.protocol.jce
import kotlinx.serialization.Polymorphic
import kotlinx.serialization.SerialId import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
@Serializable @Serializable
class SvcReqRegister( class SvcReqRegister(
@SerialId(6) var bIsOnline: Byte = 0, @SerialId(0) val lUin: Long = 0L,
@SerialId(34) var bIsSetStatus: Byte = 0, @SerialId(1) val lBid: Long = 0L,
@SerialId(7) var bIsShowOnline: Byte = 0, @SerialId(2) val cConnType: Byte = 0,
@SerialId(8) var bKikPC: Byte = 0, @SerialId(3) val sOther: String = "",
@SerialId(9) var bKikWeak: Byte = 0, @SerialId(4) val iStatus: Int = 11,
@SerialId(5) var bOnlinePush: Byte = 0, @SerialId(5) val bOnlinePush: Byte = 0,
@SerialId(22) var bOpenPush: Byte = 1, @SerialId(6) val bIsOnline: Byte = 0,
@SerialId(14) var bRegType: Byte = 0, @SerialId(7) val bIsShowOnline: Byte = 0,
@SerialId(36) var bSetMute: Byte = 0, @SerialId(8) val bKikPC: Byte = 0,
@SerialId(18) var bSlientPush: Byte = 0, @SerialId(9) val bKikWeak: Byte = 0,
@SerialId(33) var bytes_0x769_reqbody: ByteArray? = null, @SerialId(10) val timeStamp: Long = 0L,
@SerialId(2) var cConnType: Byte = 0, @SerialId(11) val iOSVersion: Long = 0L,
@SerialId(12) var cNetType: Byte = 0, @SerialId(12) val cNetType: Byte = 0,
@SerialId(23) var iLargeSeq: Long = 0L, @SerialId(13) val sBuildVer: String? = "",
@SerialId(24) var iLastWatchStartTime: Long = 0L, @SerialId(14) val bRegType: Byte = 0,
@SerialId(17) var iLocaleID: Int = 2052, @SerialId(15) val vecDevParam: ByteArray? = null,
@SerialId(11) var iOSVersion: Long = 0L, @SerialId(16) val vecGuid: ByteArray? = null,
@SerialId(4) var iStatus: Int = 11, @SerialId(17) val iLocaleID: Int = 2052,
@SerialId(1) var lBid: Long = 0L, @SerialId(18) val bSlientPush: Byte = 0,
@SerialId(29) var lCpId: Long = 0L, @SerialId(19) val strDevName: String? = null,
@SerialId(0) var lUin: Long = 0L, @SerialId(20) val strDevType: String? = null,
@SerialId(13) var sBuildVer: String? = "", @SerialId(21) val strOSVer: String? = null,
@SerialId(28) var sChannelNo: String? = "", @SerialId(22) val bOpenPush: Byte = 1,
@SerialId(3) var sOther: String = "", @SerialId(23) val iLargeSeq: Long = 0L,
@SerialId(19) var strDevName: String? = null, @SerialId(24) val iLastWatchStartTime: Long = 0L,
@SerialId(20) var strDevType: String? = null, @SerialId(26) val uOldSSOIp: Long = 0L,
@SerialId(32) var strIOSIdfa: String? = "", @SerialId(27) val uNewSSOIp: Long = 0L,
@SerialId(21) var strOSVer: String? = null, @SerialId(28) val sChannelNo: String? = "",
@SerialId(30) var strVendorName: String? = null, @SerialId(29) val lCpId: Long = 0L,
@SerialId(31) var strVendorOSName: String? = null, @SerialId(30) val strVendorName: String? = null,
@SerialId(10) var timeStamp: Long = 0L, @SerialId(31) val strVendorOSName: String? = null,
@SerialId(27) var uNewSSOIp: Long = 0L, @SerialId(32) val strIOSIdfa: String? = "",
@SerialId(26) var uOldSSOIp: Long = 0L, @SerialId(33) val bytes_0x769_reqbody: ByteArray? = null,
@SerialId(15) var vecDevParam: ByteArray? = null, @SerialId(34) val bIsSetStatus: Byte = 0,
@SerialId(16) var vecGuid: ByteArray? = null, @SerialId(35) val vecServerBuf: ByteArray? = null,
@SerialId(35) var vecServerBuf: ByteArray? = null @SerialId(36) val bSetMute: Byte = 0
// @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型 // @SerialId(25) var vecBindUin: ArrayList<*>? = null // ?? 未知泛型
) : JceStruct ) : JceStruct
\ No newline at end of file
package net.mamoe.mirai.qqandroid.network.protocol.jce
import kotlinx.io.core.BytePacketBuilder
import net.mamoe.mirai.qqandroid.io.JceOutput
import net.mamoe.mirai.qqandroid.io.buildJcePacket
import net.mamoe.mirai.qqandroid.io.writeJcePacket
inline fun BytePacketBuilder.writeUniRequestPacket(requestPacket: RequestPacket.() -> Unit) {
writeJcePacket {
RequestPacket().apply(requestPacket).writeTo(this)
}
}
\ No newline at end of file
...@@ -4,7 +4,7 @@ import kotlinx.io.core.* ...@@ -4,7 +4,7 @@ import kotlinx.io.core.*
import kotlinx.io.pool.useInstance import kotlinx.io.pool.useInstance
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.JceInput import net.mamoe.mirai.qqandroid.io.serialization.loadAs
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.OnlinePush
...@@ -248,7 +248,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf( ...@@ -248,7 +248,7 @@ internal object KnownPacketFactories : List<PacketFactory<*>> by mutableListOf(
} }
private suspend fun ByteReadPacket.parseUniResponse(bot: QQAndroidBot, packetFactory: PacketFactory<*>, ssoSequenceId: Int, consumer: PacketConsumer) { private suspend fun ByteReadPacket.parseUniResponse(bot: QQAndroidBot, packetFactory: PacketFactory<*>, ssoSequenceId: Int, consumer: PacketConsumer) {
val uni = RequestPacket.newInstanceFrom(JceInput(readIoBuffer(readInt() - 4))) val uni = readBytes(readInt() - 4).loadAs(RequestPacket.serializer())
PacketLogger.verbose(uni.toString()) PacketLogger.verbose(uni.toString())
consumer(packetFactory.decode(bot, uni.sBuffer.toReadPacket()), uni.sServantName + "." + uni.sFuncName, ssoSequenceId) consumer(packetFactory.decode(bot, uni.sBuffer.toReadPacket()), uni.sServantName + "." + uni.sFuncName, ssoSequenceId)
} }
......
...@@ -5,6 +5,9 @@ import kotlinx.io.core.discardExact ...@@ -5,6 +5,9 @@ import kotlinx.io.core.discardExact
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.serialization.Jce import net.mamoe.mirai.qqandroid.io.serialization.Jce
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestDataVersion2
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestDataVersion3
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.data.RequestPushNotify import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.data.RequestPushNotify
...@@ -20,7 +23,7 @@ class MessageSvc { ...@@ -20,7 +23,7 @@ class MessageSvc {
) )
val messageNotification = Jce.UTF8.load( val messageNotification = Jce.UTF8.load(
RequestPushNotify.serializer(), RequestPushNotify.serializer(),
req.sBuffer[0]!! req.sBuffer.loadAs(RequestDataVersion2.serializer()).map.entries.first().value.entries.first().value
) )
println(messageNotification.contentToString()) println(messageNotification.contentToString())
ProtoBuf ProtoBuf
......
...@@ -219,7 +219,11 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt ...@@ -219,7 +219,11 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse>("wt
sealed class LoginPacketResponse : Packet { sealed class LoginPacketResponse : Packet {
object Success : LoginPacketResponse() object Success : LoginPacketResponse(){
override fun toString(): String {
return "LoginPacketResponse.Success"
}
}
data class Error( data class Error(
val title: String, val title: String,
val message: String, val message: String,
......
package net.mamoe.mirai.qqandroid.network.protocol.packet.login package net.mamoe.mirai.qqandroid.network.protocol.packet.login
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.writeFully
import kotlinx.serialization.protobuf.ProtoBuf import kotlinx.serialization.protobuf.ProtoBuf
import net.mamoe.mirai.data.Packet import net.mamoe.mirai.data.Packet
import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.io.JceInput import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
import net.mamoe.mirai.qqandroid.io.jceMap
import net.mamoe.mirai.qqandroid.io.jceStruct
import net.mamoe.mirai.qqandroid.network.QQAndroidClient import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestDataStructSvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestDataVersion3
import net.mamoe.mirai.qqandroid.network.protocol.jce.RequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.jce.SvcReqRegister import net.mamoe.mirai.qqandroid.network.protocol.jce.SvcReqRegister
import net.mamoe.mirai.qqandroid.network.protocol.jce.writeUniRequestPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory import net.mamoe.mirai.qqandroid.network.protocol.packet.PacketFactory
import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.buildLoginOutgoingPacket
import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769 import net.mamoe.mirai.qqandroid.network.protocol.packet.oidb.oidb0x769.Oidb0x769
import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.writeSsoPacket
import net.mamoe.mirai.qqandroid.utils.NetworkType import net.mamoe.mirai.qqandroid.utils.NetworkType
import net.mamoe.mirai.utils.io.debugPrint
import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.encodeToString
import net.mamoe.mirai.utils.io.toReadPacket import net.mamoe.mirai.utils.io.toReadPacket
import net.mamoe.mirai.utils.localIpAddress import net.mamoe.mirai.utils.localIpAddress
...@@ -56,46 +56,45 @@ class StatSvc { ...@@ -56,46 +56,45 @@ class StatSvc {
client, subAppId = subAppId, commandName = commandName, client, subAppId = subAppId, commandName = commandName,
extraData = client.wLoginSigInfo.tgt.toReadPacket(), sequenceId = sequenceId extraData = client.wLoginSigInfo.tgt.toReadPacket(), sequenceId = sequenceId
) { ) {
JceInput writeFully(
writeUniRequestPacket { RequestPacket(
sServantName = "PushService" sServantName = "PushService",
sFuncName = "SvcReqRegister" sFuncName = "SvcReqRegister",
sBuffer = jceMap( sBuffer = RequestDataVersion3(
0, mapOf(
"SvcReqRegister" to jceStruct( "SvcReqRegister" to RequestDataStructSvcReqRegister(
0, SvcReqRegister(
SvcReqRegister().apply { cConnType = 0,
cConnType = 0 lBid = 1 or 2 or 4,
lBid = 1 or 2 or 4 lUin = client.uin,
lUin = client.uin iStatus = client.onlineStatus.id,
iStatus = client.onlineStatus.id bKikPC = 0, // 是否把 PC 踢下线
bKikPC = 0 // 是否把 PC 踢下线 bKikWeak = 0,
bKikWeak = 0 timeStamp = 0,
timeStamp = 0
// timeStamp = currentTimeSeconds // millis or seconds?? // timeStamp = currentTimeSeconds // millis or seconds??
iLargeSeq = 1551 // ? iLargeSeq = 1551, // ?
bOpenPush = 1 bOpenPush = 1,
iLocaleID = 2052 iLocaleID = 2052,
bRegType = bRegType =
(if (regPushReason == RegPushReason.appRegister || (if (regPushReason == RegPushReason.appRegister ||
regPushReason == RegPushReason.fillRegProxy || regPushReason == RegPushReason.fillRegProxy ||
regPushReason == RegPushReason.createDefaultRegInfo || regPushReason == RegPushReason.createDefaultRegInfo ||
regPushReason == RegPushReason.setOnlineStatus regPushReason == RegPushReason.setOnlineStatus
) 0 else 1).toByte() ) 0 else 1).toByte(),
bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0 bIsSetStatus = if (regPushReason == RegPushReason.setOnlineStatus) 1 else 0,
iOSVersion = client.device.version.sdk.toLong() iOSVersion = client.device.version.sdk.toLong(),
cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0 cNetType = if (client.networkType == NetworkType.WIFI) 1 else 0,
vecGuid = client.device.guid vecGuid = client.device.guid,
strDevName = client.device.model.encodeToString() strDevName = client.device.model.encodeToString(),
strDevType = client.device.model.encodeToString() strDevType = client.device.model.encodeToString(),
strOSVer = client.device.version.release.encodeToString() strOSVer = client.device.version.release.encodeToString(),
uOldSSOIp = 0 uOldSSOIp = 0,
uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String -> uNewSSOIp = localIpAddress().split(".").foldIndexed(0L) { index: Int, acc: Long, s: String ->
acc or ((s.toLong() shl (index * 16))) acc or ((s.toLong() shl (index * 16)))
} },
strVendorName = "MIUI" strVendorName = "MIUI",
strVendorOSName = "?ONEPLUS A5000_23_17" strVendorOSName = "?ONEPLUS A5000_23_17",
// register 时还需要 // register 时还需要
/* /*
var44.uNewSSOIp = field_127445; var44.uNewSSOIp = field_127445;
...@@ -116,13 +115,14 @@ class StatSvc { ...@@ -116,13 +115,14 @@ class StatSvc {
) )
) )
) )
) ),
bSetMute = 0 bSetMute = 0
}
) )
).toByteArray(RequestDataStructSvcReqRegister.serializer())
)
).toByteArray(RequestDataVersion3.serializer())
).toByteArray(RequestPacket.serializer())
) )
}
this.writePacket(this.build().debugPrint("sso body"))
} }
} }
......
package net.mamoe.mirai.qqandroid.io.serialization package net.mamoe.mirai.qqandroid.io.serialization
import kotlinx.io.core.readBytes
import kotlinx.serialization.SerialId import kotlinx.serialization.SerialId
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import net.mamoe.mirai.qqandroid.io.CharsetUTF8
import net.mamoe.mirai.qqandroid.io.JceOutput
import net.mamoe.mirai.qqandroid.io.JceStruct import net.mamoe.mirai.qqandroid.io.JceStruct
import net.mamoe.mirai.qqandroid.io.buildJcePacket import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.io.toUHexString
import kotlin.test.Test import kotlin.test.Test
import kotlin.test.assertEquals
class JceEncoderTest { class JceDecoderTest {
@Serializable @Serializable
class TestSimpleJceStruct( class TestSimpleJceStruct(
...@@ -23,51 +18,16 @@ class JceEncoderTest { ...@@ -23,51 +18,16 @@ class JceEncoderTest {
@SerialId(4) val long: Long = 123, @SerialId(4) val long: Long = 123,
@SerialId(5) val float: Float = 123f, @SerialId(5) val float: Float = 123f,
@SerialId(6) val double: Double = 123.0 @SerialId(6) val double: Double = 123.0
) : JceStruct() { ) : JceStruct
override fun writeTo(builder: JceOutput) = builder.run {
writeString("123", 0)
writeByte(123, 1)
writeShort(123, 2)
writeInt(123, 3)
writeLong(123, 4)
writeFloat(123f, 5)
writeDouble(123.0, 6)
}
}
@Test @Test
fun testEncoder() { fun testEncoder() {
assertEquals( println(TestComplexJceStruct().toByteArray(TestComplexJceStruct.serializer()).loadAs(TestComplexJceStruct.serializer()).contentToString())
buildJcePacket {
writeString("123", 0)
writeByte(123, 1)
writeShort(123, 2)
writeInt(123, 3)
writeLong(123, 4)
writeFloat(123f, 5)
writeDouble(123.0, 6)
}.readBytes().toUHexString(),
Jce.GBK.dump(
TestSimpleJceStruct.serializer(),
TestSimpleJceStruct()
).toUHexString()
)
} }
@Test @Test
fun testEncoder2() { fun testEncoder2() {
assertEquals(
buildJcePacket(stringCharset = CharsetUTF8) {
writeFully(byteArrayOf(1, 2, 3), 7)
writeCollection(listOf(1, 2, 3), 8)
writeMap(mapOf("哈哈" to "嘿嘿"), 9)
writeJceStruct(TestSimpleJceStruct(), 10)
}.readBytes().toUHexString(),
Jce.UTF8.dump(
TestComplexJceStruct.serializer(),
TestComplexJceStruct()
).toUHexString()
)
} }
@Serializable @Serializable
...@@ -76,5 +36,5 @@ class JceEncoderTest { ...@@ -76,5 +36,5 @@ class JceEncoderTest {
@SerialId(8) val byteList: List<Byte> = listOf(1, 2, 3), @SerialId(8) val byteList: List<Byte> = listOf(1, 2, 3),
@SerialId(9) val map: Map<String, String> = mapOf("哈哈" to "嘿嘿"), @SerialId(9) val map: Map<String, String> = mapOf("哈哈" to "嘿嘿"),
@SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct() @SerialId(10) val nestedJceStruct: TestSimpleJceStruct = TestSimpleJceStruct()
) ) : JceStruct
} }
\ No newline at end of file
package test
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.util.zip.InflaterInputStream
object QLogReader {
@JvmStatic
fun main(args: Array<String>) {
println(readQLog(File("C:\\Users\\Him18\\Desktop\\log\\wtlogin_20200101.log")))
}
fun readQLog(file: File): String {
return (decompress(file.readBytes()))
}
fun decompress(array: ByteArray): String {
return buildString {
if (array.isNotEmpty()) {
var i = 0
var n = 0
while (array.size > n + 3) {
val buf_to_int32: Int = buf_to_int32(array, n)
if (array.size <= n + buf_to_int32 + 3) {
break
}
val buf = ByteArray(buf_to_int32)
System.arraycopy(array, n + 4, buf, 0, buf_to_int32)
n += 4 + buf_to_int32
++i
val byteArrayOutputStream = ByteArrayOutputStream()
val `in` = ByteArrayInputStream(buf)
val inflaterInputStream = InflaterInputStream(`in`)
val array2 = ByteArray(1024)
while (true) {
val read = inflaterInputStream.read(array2)
if (read == -1) {
break
}
byteArrayOutputStream.write(array2, 0, read)
}
append(byteArrayOutputStream.toString())
}
}
}
}
private fun buf_to_int32(array: ByteArray, n: Int): Int {
return (array[n].toInt() shl 24 and -0x1000000) + (array[n + 1].toInt() shl 16 and 0xFF0000) + (array[n + 2].toInt() shl 8 and 0xFF00) + (array[n + 3].toInt() shl 0 and 0xFF)
}
}
\ No newline at end of file
...@@ -389,7 +389,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor ...@@ -389,7 +389,7 @@ internal class TIMPCBotNetworkHandler internal constructor(coroutineContext: Cor
close() close()
return return
} }
val code = configuration.loginSolver(bot, captchaCache!!) val code = configuration.loginSolver.onSolvePicCaptcha(bot, captchaCache!!)
this.captchaCache = null this.captchaCache = null
if (code == null || code.length != 4) { if (code == null || code.length != 4) {
......
package net.mamoe.mirai.utils
/**
* 直接抛出异常. 需自行处理验证码, 在 [BotConfiguration.captchaSolver] 中调整
*/
actual var DefaultCaptchaSolver: CaptchaSolver = {
error("No CaptchaSolver found. BotConfiguration.captchaSolver should be assigned manually")
}
\ No newline at end of file
package net.mamoe.mirai.utils
import kotlinx.io.core.IoBuffer
import net.mamoe.mirai.Bot
/**
* 在各平台实现的默认的验证码处理器.
*/
actual var defaultLoginSolver: LoginSolver = object : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
error("should be implemented manually by you")
}
override suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String? {
error("should be implemented manually by you")
}
override suspend fun onGetPhoneNumber(): String {
error("should be implemented manually by you")
}
override suspend fun onGetSMSVerifyCode(): String {
error("should be implemented manually by you")
}
}
\ No newline at end of file
...@@ -28,7 +28,7 @@ import kotlin.coroutines.CoroutineContext ...@@ -28,7 +28,7 @@ import kotlin.coroutines.CoroutineContext
actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver() actual var defaultLoginSolver: LoginSolver = DefaultLoginSolver()
class DefaultLoginSolver(): LoginSolver(){ class DefaultLoginSolver : LoginSolver() {
override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? { override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
loginSolverLock.withLock { loginSolverLock.withLock {
val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() } val tempFile: File = createTempFile(suffix = ".png").apply { deleteOnExit() }
......
package jceTest
import io.ktor.util.InternalAPI
import jce.jce.JceInputStream
import jceTest.JceOutputTest.TestMiraiStruct
import jceTest.JceOutputTest.TestQQStruct
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
import net.mamoe.mirai.utils.cryptor.contentToString
import net.mamoe.mirai.utils.io.toIoBuffer
import org.junit.Test
private infix fun <T> T.shouldEqualTo(another: T) {
if (this is Array<*>) {
this.contentEquals(another as Array<*>)
} else
check(this.contentToString() == another.contentToString()) {
"""actual: ${this.contentToString()}
|required: ${another.contentToString()}
""".trimMargin()
}
}
@UseExperimental(InternalAPI::class)
private fun <R> ByteArray.qqJce(block: JceInputStream.() -> R): R {
return JceInputStream(this).run(block)
}
private fun <R> ByteArray.read(block: JceInput.() -> R): R {
return JceInput(this.toIoBuffer()).run(block)
}
private fun ByteReadPacket.check(block: ByteArray.() -> Unit) {
this.readBytes().apply(block)
}
internal class JceInputTest {
@Test
fun readByte() = buildJcePacket {
writeByte(1, 1)
}.check {
read {
readByte(1)
} shouldEqualTo qqJce {
read(0.toByte(), 1, true)
}
}
@Test
fun readDouble() = buildJcePacket {
writeDouble(1.0, 1)
}.check {
read {
readDouble(1)
} shouldEqualTo qqJce {
read(0.toDouble(), 1, true)
}
}
@Test
fun readFloat() = buildJcePacket {
writeFloat(1.0f, 1)
}.check {
read {
readFloat(1)
} shouldEqualTo qqJce {
read(0.toFloat(), 1, true)
}
}
@Test
fun readFully() = buildJcePacket {
writeFully(byteArrayOf(1, 2, 3), 1)
}.check {
read {
readByteArray(1)
} shouldEqualTo qqJce {
read(byteArrayOf(), 1, true)
}
}
@Test
fun testWriteFully() = buildJcePacket {
writeFully(shortArrayOf(1, 2, 3), 1)
}.check {
read {
readShortArray(1)
} shouldEqualTo qqJce {
read(shortArrayOf(), 1, true)
}
}
@Test
fun testWriteFully1() = buildJcePacket {
writeFully(intArrayOf(1, 2, 3), 1)
}.check {
read {
readIntArray(1)
} shouldEqualTo qqJce {
read(intArrayOf(), 1, true)
}
}
@Test
fun testWriteFully2() = buildJcePacket {
writeFully(longArrayOf(1, 2, 3), 1)
}.check {
read {
readLongArray(1)
} shouldEqualTo qqJce {
read(longArrayOf(), 1, true)
}
}
@Test
fun testWriteFully3() = buildJcePacket {
writeFully(booleanArrayOf(true, false, true), 1)
}.check {
read {
readBooleanArray(1)
} shouldEqualTo qqJce {
read(booleanArrayOf(), 1, true)
}
}
@Test
fun testWriteFully4() = buildJcePacket {
writeFully(floatArrayOf(1f, 2f, 3f), 1)
}.check {
read {
readFloatArray(1)
} shouldEqualTo qqJce {
read(floatArrayOf(), 1, true)
}
}
@Test
fun testWriteFully5() = buildJcePacket {
writeFully(doubleArrayOf(1.0, 2.0, 3.0), 1)
}.check {
read {
readDoubleArray(1)
} shouldEqualTo qqJce {
read(doubleArrayOf(), 1, true)
}
}
@Test
fun testWriteFully6() = buildJcePacket {
writeFully(arrayOf("sss", "哈哈"), 1)
}.check {
read {
readSimpleArray("", 1)
} shouldEqualTo qqJce {
read(arrayOf(""), 1, true)
}
}
@Test
fun testWriteFully7() = buildJcePacket {
writeFully(arrayOf("sss", "哈哈"), 1)
}.check {
read {
readArrayOrNull("", 1)!!
} shouldEqualTo qqJce {
read(arrayOf(""), 1, true)
}
}
@Test
fun testWriteFully8() = buildJcePacket {
writeFully(arrayOf(TestMiraiStruct("Haha")), 1)
}.check {
read {
readJceStructArrayOrNull(TestMiraiStruct, 1)!!
} shouldEqualTo qqJce {
read(arrayOf(TestQQStruct("stub")), 1, true)
}
}
@Test
fun readInt() = buildJcePacket {
writeInt(1, 2)
}.check {
read {
readInt(2)
} shouldEqualTo qqJce {
read(0, 2, true)
}
}
@Test
fun readLong() = buildJcePacket {
writeLong(1, 2)
}.check {
read {
readLong(2)
} shouldEqualTo qqJce {
read(0L, 2, true)
}
}
@Test
fun readShort() = buildJcePacket {
writeShort(1, 2)
}.check {
read {
readShort(2)
} shouldEqualTo qqJce {
read(0.toShort(), 2, true)
}
}
@Test
fun readBoolean() = buildJcePacket {
writeBoolean(true, 2)
}.check {
read {
readBoolean(2)
} shouldEqualTo qqJce {
read(false, 2, true)
}
}
@Test
fun readString() = buildJcePacket {
writeString("嗨", 2)
}.check {
read {
readString(2)
} shouldEqualTo qqJce {
read("", 2, true)
}
}
@Test
fun readMap() = buildJcePacket {
writeMap(mapOf(123.0 to "Hello"), 3)
}.check {
read {
readMap(0.0, "", 3)
} shouldEqualTo qqJce {
read(mapOf(0.0 to ""), 3, true)
}
}
@Test
fun readCollection() = buildJcePacket {
writeCollection(listOf("1", "还"), 3)
}.check {
repeat(0) {
error("fuck kotlin")
}
read {
readList("", 3)
} shouldEqualTo qqJce {
read(listOf(""), 3, true)
}
}
@Test
fun readJceStruct() = buildJcePacket {
writeJceStruct(TestMiraiStruct("123"), 3)
}.check {
read {
readJceStruct(TestMiraiStruct, 3)
} shouldEqualTo qqJce {
read(TestQQStruct("stub"), 3, true)!!
}
}
@Test
fun readObject() = buildJcePacket {
writeObject(123, 3)
}.check {
read {
readObject(123, 3)
} shouldEqualTo qqJce {
read(123 as Any, 3, true)
}
}
}
\ No newline at end of file
package jceTest
import io.ktor.util.InternalAPI
import jce.jce.JceInputStream
import jce.jce.JceOutputStream
import jce.jce.JceStruct
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.readBytes
import net.mamoe.mirai.qqandroid.network.io.JceInput
import net.mamoe.mirai.qqandroid.network.io.JceOutput
import net.mamoe.mirai.qqandroid.network.io.buildJcePacket
import net.mamoe.mirai.utils.io.toUHexString
import org.junit.Test
private infix fun ByteReadPacket.shouldEqualTo(another: ByteArray) {
this.readBytes().let {
check(it.contentEquals(another)) {
"""actual: ${it.toUHexString()}
|required: ${another.toUHexString()}
""".trimMargin()
}
}
}
@UseExperimental(InternalAPI::class)
private fun qqJce(block: JceOutputStream.() -> Unit): ByteArray {
return JceOutputStream().apply(block).toByteArray()
}
internal class JceOutputTest {
@Test
fun writeByte() {
buildJcePacket {
writeByte(1, 1)
writeByte(-128, 2)
} shouldEqualTo qqJce {
write(1.toByte(), 1)
write((-128).toByte(), 2)
}
}
@Test
fun writeDouble() {
buildJcePacket {
writeDouble(1.0, 1)
writeDouble(-128.0, 2)
} shouldEqualTo qqJce {
write(1.toDouble(), 1)
write((-128).toDouble(), 2)
}
}
@Test
fun writeFloat() {
buildJcePacket {
writeFloat(1.0f, 1)
writeFloat(-128.0f, 2)
} shouldEqualTo qqJce {
write(1.toFloat(), 1)
write((-128).toFloat(), 2)
}
}
@Test
fun writeFully() {
buildJcePacket {
writeFully(byteArrayOf(1, 2), 1)
writeFully(byteArrayOf(1, 2), 2)
} shouldEqualTo qqJce {
write(byteArrayOf(1, 2), 1)
write(byteArrayOf(1, 2), 2)
}
}
@Test
fun testWriteFully() {
buildJcePacket {
writeFully(intArrayOf(1, 2), 1)
writeFully(intArrayOf(1, 2), 2)
} shouldEqualTo qqJce {
write(intArrayOf(1, 2), 1)
write(intArrayOf(1, 2), 2)
}
}
@Test
fun testWriteFully1() {
buildJcePacket {
writeFully(shortArrayOf(1, 2), 1)
writeFully(shortArrayOf(1, 2), 2)
} shouldEqualTo qqJce {
write(shortArrayOf(1, 2), 1)
write(shortArrayOf(1, 2), 2)
}
}
@Test
fun testWriteFully2() {
buildJcePacket {
writeFully(booleanArrayOf(true, false), 1)
writeFully(booleanArrayOf(true, false), 2)
} shouldEqualTo qqJce {
write(booleanArrayOf(true, false), 1)
write(booleanArrayOf(true, false), 2)
}
}
@Test
fun testWriteFully3() {
buildJcePacket {
writeFully(longArrayOf(1, 2), 1)
writeFully(longArrayOf(1, 2), 2)
} shouldEqualTo qqJce {
write(longArrayOf(1, 2), 1)
write(longArrayOf(1, 2), 2)
}
}
@Test
fun testWriteFully4() {
buildJcePacket {
writeFully(floatArrayOf(1f, 2f), 1)
writeFully(floatArrayOf(1f, 2f), 2)
} shouldEqualTo qqJce {
write(floatArrayOf(1f, 2f), 1)
write(floatArrayOf(1f, 2f), 2)
}
}
@Test
fun testWriteFully5() {
buildJcePacket {
writeFully(doubleArrayOf(1.0, 2.0), 1)
writeFully(doubleArrayOf(1.0, 2.0), 2)
} shouldEqualTo qqJce {
write(doubleArrayOf(1.0, 2.0), 1)
write(doubleArrayOf(1.0, 2.0), 2)
}
}
@Test
fun testWriteFully6() {
buildJcePacket {
writeFully(arrayOf("123", "哈哈"), 1)
writeFully(arrayOf("123", "哈哈"), 2)
} shouldEqualTo qqJce {
write(arrayOf("123", "哈哈"), 1)
write(arrayOf("123", "哈哈"), 2)
}
}
@Test
fun writeInt() {
buildJcePacket {
writeInt(1, 1)
writeInt(-128, 2)
} shouldEqualTo qqJce {
write(1, 1)
write(-128, 2)
}
}
@Test
fun writeLong() {
buildJcePacket {
writeLong(1, 1)
writeLong(-128, 2)
} shouldEqualTo qqJce {
write(1L, 1)
write(-128L, 2)
}
}
@Test
fun writeShort() {
buildJcePacket {
writeShort(1, 1)
writeShort(-128, 2)
} shouldEqualTo qqJce {
write(1.toShort(), 1)
write((-128).toShort(), 2)
}
}
@Test
fun writeBoolean() {
buildJcePacket {
writeBoolean(true, 1)
writeBoolean(false, 2)
} shouldEqualTo qqJce {
write(true, 1)
write(false, 2)
}
}
@Test
fun writeString() {
buildJcePacket {
writeString("1", 1)
writeString("哈啊", 2)
} shouldEqualTo qqJce {
write("1", 1)
write("哈啊", 2)
}
}
@Test
fun writeMap() {
buildJcePacket {
writeMap(mapOf("" to ""), 1)
writeMap(mapOf("" to 123), 2)
writeMap(mapOf(123.0 to "Hello"), 3)
} shouldEqualTo qqJce {
write(mapOf("" to ""), 1)
write(mapOf("" to 123), 2)
write(mapOf(123.0 to "Hello"), 3)
}
}
@Test
fun writeCollection() {
buildJcePacket {
writeCollection(listOf("啊", "333", "1"), 1)
} shouldEqualTo qqJce {
write(listOf("啊", "333", "1"), 1)
}
}
data class TestMiraiStruct(
val message: String
) : net.mamoe.mirai.qqandroid.network.io.JceStruct() {
override fun writeTo(builder: JceOutput) {
builder.writeString(message, 0)
}
companion object : Factory<TestMiraiStruct> {
override fun newInstanceFrom(input: JceInput): TestMiraiStruct {
return TestMiraiStruct(input.readString(0))
}
}
}
class TestQQStruct(
private var message: String
) : JceStruct() {
constructor() : this("")
override fun readFrom(var1: JceInputStream) {
message = var1.read("", 0, true)
}
override fun writeTo(var1: JceOutputStream) {
var1.write(message, 0)
}
override fun toString(): String {
return "TestMiraiStruct(message=$message)"
}
}
@Test
fun writeJceStruct() {
buildJcePacket {
writeJceStruct(TestMiraiStruct("Hello"), 0)
writeJceStruct(TestMiraiStruct("嗨"), 1)
} shouldEqualTo qqJce {
write(TestQQStruct("Hello"), 0)
write(TestQQStruct("嗨"), 1)
}
}
@Test
fun writeObject() {
buildJcePacket {
writeObject(0.toByte(), 1)
writeObject(0.toShort(), 2)
writeObject(0, 3)
writeObject(0L, 4)
writeObject(0f, 5)
writeObject(0.0, 6)
writeObject("hello", 7)
writeObject(TestMiraiStruct("Hello"), 8)
} shouldEqualTo qqJce {
write(0.toByte(), 1)
write(0.toShort(), 2)
write(0, 3)
write(0L, 4)
write(0f, 5)
write(0.0, 6)
write("hello", 7)
write(TestQQStruct("Hello"), 8)
}
}
}
\ No newline at end of file
...@@ -10,11 +10,13 @@ import android.os.IBinder ...@@ -10,11 +10,13 @@ import android.os.IBinder
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.io.core.IoBuffer
import kotlinx.io.core.readBytes import kotlinx.io.core.readBytes
import net.mamoe.mirai.Bot import net.mamoe.mirai.Bot
import net.mamoe.mirai.event.subscribeMessages import net.mamoe.mirai.event.subscribeMessages
import net.mamoe.mirai.timpc.TIMPC import net.mamoe.mirai.timpc.TIMPC
import net.mamoe.mirai.utils.LoginFailedException import net.mamoe.mirai.utils.LoginFailedException
import net.mamoe.mirai.utils.LoginSolver
import java.lang.ref.WeakReference import java.lang.ref.WeakReference
class MiraiService : Service() { class MiraiService : Service() {
...@@ -42,12 +44,27 @@ class MiraiService : Service() { ...@@ -42,12 +44,27 @@ class MiraiService : Service() {
private fun login(qq: Long, pwd: String) { private fun login(qq: Long, pwd: String) {
GlobalScope.launch { GlobalScope.launch {
mBot = TIMPC.Bot(qq, pwd) { mBot = TIMPC.Bot(qq, pwd) {
captchaSolver = { loginSolver = object : LoginSolver() {
val bytes = it.readBytes() override suspend fun onSolvePicCaptcha(bot: Bot, data: IoBuffer): String? {
val bytes = data.readBytes()
val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size) val bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
mCaptchaDeferred = CompletableDeferred() mCaptchaDeferred = CompletableDeferred()
mCallback?.get()?.onCaptcha(bitmap) mCallback?.get()?.onCaptcha(bitmap)
mCaptchaDeferred.await() return mCaptchaDeferred.await()
}
override suspend fun onSolveSliderCaptcha(bot: Bot, data: IoBuffer): String? {
TODO("not implemented")
}
override suspend fun onGetPhoneNumber(): String {
TODO("not implemented")
}
override suspend fun onGetSMSVerifyCode(): String {
TODO("not implemented")
}
} }
}.apply { }.apply {
try { try {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment