mirror of
https://github.com/mamoe/mirai.git
synced 2025-03-23 22:00:10 +08:00
Android stuff
This commit is contained in:
parent
82f1fda69c
commit
45d0724391
@ -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")
|
||||
}
|
||||
|
@ -1,21 +1,90 @@
|
||||
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
|
||||
|
@ -1,15 +1,15 @@
|
||||
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()
|
||||
}
|
||||
) : QQAndroidBotBase(context, account, configuration)
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user