Commit 45d07243 authored by Him188's avatar Him188

Android stuff

parent 82f1fda6
......@@ -7,20 +7,26 @@ import net.mamoe.mirai.data.AddFriendResult
import net.mamoe.mirai.data.ImageLink
import net.mamoe.mirai.message.data.Image
import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
import net.mamoe.mirai.qqandroid.network.QQAndroidClient
import net.mamoe.mirai.qqandroid.utils.Context
import net.mamoe.mirai.utils.BotConfiguration
import net.mamoe.mirai.utils.MiraiInternalAPI
import kotlin.coroutines.CoroutineContext
internal expect class QQAndroidBot(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : QQAndroidBotBase
@UseExperimental(MiraiInternalAPI::class)
internal abstract class QQAndroidBotBase constructor(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : BotImpl<QQAndroidBotNetworkHandler>(account, configuration) {
val client: QQAndroidClient = QQAndroidClient(context, account)
override val qqs: ContactList<QQ>
get() = TODO("not implemented")
......@@ -47,10 +53,6 @@ internal abstract class QQAndroidBotBase constructor(
TODO("not implemented")
}
override suspend fun login() {
TODO("not implemented")
}
override suspend fun Image.getLink(): ImageLink {
TODO("not implemented")
}
......
package net.mamoe.mirai.qqandroid.network
import kotlinx.coroutines.CompletableJob
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.*
import kotlinx.io.core.*
import kotlinx.io.pool.useInstance
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.qqandroid.QQAndroidBot
import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket
import net.mamoe.mirai.utils.io.*
import kotlin.coroutines.CoroutineContext
internal class QQAndroidBotNetworkHandler(override val bot: QQAndroidBot) : BotNetworkHandler() {
override val supervisor: CompletableJob = SupervisorJob(bot.coroutineContext[Job])
private val channel: PlatformDatagramChannel = PlatformDatagramChannel("wtlogin.qq.com", 8000)
override suspend fun login() {
TODO("not implemented")
launch { processReceive() }
val buffer = IoBuffer.Pool.borrow()
buffer.writePacket(LoginPacket(bot.client).delegate)
val shouldBeSent = buffer.readRemaining
check(channel.send(buffer) == shouldBeSent) {
"Buffer is not entirely sent. " +
"Required sent length=$shouldBeSent, but after channel.send, " +
"buffer remains ${buffer.readBytes().toUHexString()}"
}
buffer.release(IoBuffer.Pool)
println("Login sent")
}
private suspend fun processReceive() {
while (channel.isOpen) {
val buffer = IoBuffer.Pool.borrow()
try {
channel.read(buffer)// JVM: withContext(IO)
} catch (e: ClosedChannelException) {
dispose()
return
} catch (e: ReadPacketInternalException) {
bot.logger.error("Socket channel read failed: ${e.message}")
continue
} catch (e: CancellationException) {
return
} catch (e: Throwable) {
bot.logger.error("Caught unexpected exceptions", e)
continue
} finally {
if (!buffer.canRead() || buffer.readRemaining == 0) {//size==0
//bot.logger.debug("processReceive: Buffer cannot be read")
buffer.release(IoBuffer.Pool)
continue
}// sometimes exceptions are thrown without this `if` clause
}
//buffer.resetForRead()
launch(CoroutineName("handleServerPacket")) {
// `.use`: Ensure that the packet is consumed **totally**
// so that all the buffers are released
ByteArrayPool.useInstance {
val length = buffer.readRemaining - 1
buffer.readFully(it, 0, length)
buffer.resetForWrite()
buffer.writeFully(it, 0, length)
}
ByteReadPacket(buffer, IoBuffer.Pool).use { input ->
try {
input.debugPrint("Received")
} catch (e: Exception) {
bot.logger.error(e)
}
}
}
}
}
override suspend fun awaitDisconnection() {
TODO()
while (true) {
delay(100)
// TODO: 2019/12/31
}
}
override fun dispose(cause: Throwable?) {
println("Closed")
super.dispose(cause)
}
override val coroutineContext: CoroutineContext = bot.coroutineContext
......
......@@ -3,6 +3,7 @@ package net.mamoe.mirai.qqandroid.network
import kotlinx.io.core.toByteArray
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.qqandroid.utils.*
import net.mamoe.mirai.utils.io.hexToBytes
/*
APP ID:
......@@ -41,7 +42,7 @@ internal open class QQAndroidClient(
var networkType: NetworkType = NetworkType.WIFI
val apkSignatureMd5: ByteArray = TODO()
val apkSignatureMd5: ByteArray = "A6 B7 45 BF 24 A2 C2 77 52 77 16 F6 F3 6E B6 8D".hexToBytes()
/**
* 协议版本?, 8.2.0 的为 8001
......
......@@ -96,6 +96,9 @@ fun BytePacketBuilder.t106(
loginType: LoginType
) {
writeShort(0x106)
passwordMd5.requireSize(16)
tgtgtKey.requireSize(16)
guid?.requireSize(16)
writeShortLVPacket {
encryptAndWrite(md5(passwordMd5 + (salt.takeIf { it != 0L } ?: uin).toInt().toByteArray())) {
......@@ -132,7 +135,7 @@ fun BytePacketBuilder.t106(
writeInt(loginType.value)
writeShortLVByteArray(uinAccount) // TODO check if should be empty byte[]
}
} shouldEqualsTo 98
}
}
fun BytePacketBuilder.t116(
......@@ -620,7 +623,8 @@ fun BytePacketBuilder.t318(
private fun Boolean.toByte(): Byte = if (this) 1 else 0
private fun Boolean.toInt(): Int = if (this) 1 else 0
private infix fun Int.shouldEqualsTo(int: Int) = require(this == int)
private infix fun Int.shouldEqualsTo(int: Int) = require(this == int) { "Required $int, but found $this" }
private fun ByteArray.requireSize(exactSize: Int) = require(this.size == exactSize) { "Required size $exactSize, but found ${this.size}" }
fun randomAndroidId(): String = buildString(15) {
repeat(15) { append(Random.nextInt(10)) }
......
......@@ -21,6 +21,9 @@ class LoginPacketDecrypter(override val value: ByteArray) : DecrypterByteArray {
@UseExperimental(ExperimentalUnsignedTypes::class)
internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, LoginPacketDecrypter>(LoginPacketDecrypter) {
init {
this._id = PacketId(0x0810, 9)
}
operator fun invoke(
client: QQAndroidClient
......@@ -144,6 +147,15 @@ internal object LoginPacket : PacketFactory<LoginPacket.LoginPacketResponse, Log
}
}
@Suppress("FunctionName")
fun PacketId(commandId: Int, subCommandId: Int) = object : PacketId {
override val commandId: Int
get() = commandId
override val subCommandId: Int
get() = subCommandId
}
interface PacketId {
val commandId: Int // ushort actually
val subCommandId: Int // ushort actually
......
package net.mamoe.mirai.qqandroid
import net.mamoe.mirai.BotAccount
import net.mamoe.mirai.alsoLogin
import net.mamoe.mirai.qqandroid.utils.Context
import net.mamoe.mirai.qqandroid.utils.ContextImpl
import net.mamoe.mirai.utils.BotConfiguration
@Suppress("FunctionName")
internal fun QQAndroidBot(account: BotAccount, configuration: BotConfiguration) = QQAndroidBot(ContextImpl(), account, configuration)
internal actual class QQAndroidBot actual constructor(
context: Context,
account: BotAccount,
configuration: BotConfiguration
) : QQAndroidBotBase(account, configuration)
suspend fun main() {
val bot = QQAndroidBot(BotAccount(1, ""), BotConfiguration()).alsoLogin()
bot.network.awaitDisconnection()
}
\ No newline at end of file
) : QQAndroidBotBase(context, account, configuration)
\ No newline at end of file
......@@ -16,13 +16,14 @@ import kotlinx.serialization.internal.ArrayListSerializer
import kotlinx.serialization.json.Json
import net.mamoe.mirai.Bot
import net.mamoe.mirai.network.BotNetworkHandler
import net.mamoe.mirai.timpc.TIMPC
import net.mamoe.mirai.timpc.network.TIMProtocol
import net.mamoe.mirai.timpc.network.packet.*
import net.mamoe.mirai.timpc.network.packet.event.FriendOnlineStatusChangedPacket
import net.mamoe.mirai.timpc.network.packet.event.IgnoredEventPacket
import net.mamoe.mirai.timpc.network.packet.login.*
import net.mamoe.mirai.timpc.network.packet.login.CaptchaKey
import net.mamoe.mirai.timpc.network.packet.login.HeartbeatPacket
import net.mamoe.mirai.timpc.network.packet.login.ShareKey
import net.mamoe.mirai.timpc.network.packet.login.TouchKey
import net.mamoe.mirai.utils.cryptor.Decrypter
import net.mamoe.mirai.utils.cryptor.DecryptionFailedException
import net.mamoe.mirai.utils.cryptor.NoDecrypter
......@@ -127,7 +128,7 @@ suspend fun main() {
listenDevice(localIp, it)
}
println("Using sessionKey = ${sessionKey.value.toUHexString()}")
println("Filter QQ = ${qq?.toLong()}")
println("Filter QQ = $qq")
PacketDebugger.recorder?.let { println("Recorder is enabled") }
Runtime.getRuntime().addShutdownHook(thread(false) {
PacketDebugger.recorder?.writeTo(File(GMTDate().toString() + ".record"))?.also { println("${PacketDebugger.recorder.list.size} records saved.") }
......@@ -183,8 +184,7 @@ internal object PacketDebugger {
* 7. 运行完 `mov eax,dword ptr ss:[ebp+10]`
* 8. 查看内存, `eax` 到 `eax+10` 的 16 字节就是 `sessionKey`
*/
val sessionKey: SessionKey =
SessionKey("D8 D0 B0 DE 37 53 9B 05 A5 E7 AB 96 B2 AC AD EC".hexToBytes())
val sessionKey: SessionKey get() = SessionKey("D8 D0 B0 DE 37 53 9B 05 A5 E7 AB 96 B2 AC AD EC".hexToBytes())
// TODO: 2019/12/7 无法访问 internal 是 kotlin bug, KT-34849
/**
......@@ -197,9 +197,9 @@ internal object PacketDebugger {
val recorder: Recorder? = Recorder()
val IgnoredPacketIdList: List<PacketId> = listOf(
KnownPacketId.get<FriendOnlineStatusChangedPacket>(),
KnownPacketId.get<ChangeOnlineStatusPacket>(),
KnownPacketId.get<HeartbeatPacket>()
// KnownPacketId.get<FriendOnlineStatusChangedPacket>(),
// KnownPacketId.get<ChangeOnlineStatusPacket>(),
// KnownPacketId.get<HeartbeatPacket>()
)
suspend fun dataReceived(data: ByteArray) {
......@@ -304,7 +304,7 @@ internal object PacketDebugger {
// 3E 03 3F A2 02 00 00 00 01 2E 01 00 00 69 35
discardExact(3)//head
val id = net.mamoe.mirai.timpc.network.packet.matchPacketId(readUShort())
val id = matchPacketId(readUShort())
val sequence = readUShort().toUHexString()
if (IgnoredPacketIdList.contains(id)) {
return
......
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