@file:OptIn(DelicateCoroutinesApi::class) package moe.fuqiuluo.qqinterface.servlet import android.os.Bundle import com.tencent.mobileqq.app.QQAppInterface import com.tencent.mobileqq.msf.core.MsfCore import com.tencent.mobileqq.msf.service.MsfService import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.qphone.base.remote.ToServiceMsg import com.tencent.qqnt.kernel.api.IKernelService import io.ktor.utils.io.core.BytePacketBuilder import io.ktor.utils.io.core.readBytes import io.ktor.utils.io.core.writeFully import io.ktor.utils.io.core.writeInt import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.launch import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import kotlinx.serialization.encodeToByteArray import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.utils.DeflateTools import moe.fuqiuluo.shamrock.utils.PlatformUtils import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher import moe.fuqiuluo.shamrock.xposed.helper.internal.DynamicReceiver import moe.fuqiuluo.shamrock.xposed.helper.internal.IPCRequest import protobuf.oidb.TrpcOidb import mqq.app.MobileQQ import protobuf.auto.toByteArray import tencent.im.oidb.oidb_sso import kotlin.coroutines.CoroutineContext import kotlin.coroutines.resume internal abstract class BaseSvc { companion object Default: CoroutineScope { val currentUin: String get() = app.currentAccountUin val app: QQAppInterface get() = AppRuntimeFetcher.appRuntime as QQAppInterface fun createToServiceMsg(cmd: String): ToServiceMsg { return ToServiceMsg("mobileqq.service", app.currentAccountUin, cmd) } suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? { val seq = MsfService.getCore().nextSeq val buffer = withTimeoutOrNull(timeout) { suspendCancellableCoroutine { continuation -> launch(Dispatchers.Default) { DynamicReceiver.register(IPCRequest(cmd, seq) { val buffer = it.getByteArrayExtra("buffer")!! continuation.resume(buffer) }) } if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq) else sendOidb(cmd, cmdId, serviceId, data, seq) } }.also { if (it == null) DynamicReceiver.unregister(seq) }?.copyOf() try { if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) { val builder = BytePacketBuilder() val deBuffer = DeflateTools.uncompress(buffer.slice(4)) builder.writeInt(deBuffer.size) builder.writeFully(deBuffer) return builder.build().readBytes() } } catch (_: Exception) { } return buffer } suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? { val seq = MsfService.getCore().nextSeq val buffer = withTimeoutOrNull(timeout) { suspendCancellableCoroutine { continuation -> launch(Dispatchers.Default) { DynamicReceiver.register(IPCRequest(cmd, seq) { val buffer = it.getByteArrayExtra("buffer")!! continuation.resume(buffer) }) sendBuffer(cmd, isPb, data, seq) } } }.also { if (it == null) DynamicReceiver.unregister(seq) }?.copyOf() try { if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) { val builder = BytePacketBuilder() val deBuffer = DeflateTools.uncompress(buffer.slice(4)) builder.writeInt(deBuffer.size) builder.writeFully(deBuffer) return builder.build().readBytes() } } catch (_: Exception) { } return buffer } fun sendOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1, trpc: Boolean = false) { if (trpc) { sendTrpcOidb(cmd, cmdId, serviceId, buffer, seq) return } val to = createToServiceMsg(cmd) val oidb = oidb_sso.OIDBSSOPkg() oidb.uint32_command.set(cmdId) oidb.uint32_service_type.set(serviceId) oidb.bytes_bodybuffer.set(ByteStringMicro.copyFrom(buffer)) oidb.str_client_version.set(PlatformUtils.getClientVersion(MobileQQ.getContext())) to.putWupBuffer(oidb.toByteArray()) to.addAttribute("req_pb_protocol_flag", true) if (seq != -1) { to.addAttribute("shamrock_seq", seq) } app.sendToService(to) } fun sendTrpcOidb(cmd: String, cmdId: Int, serviceId: Int, buffer: ByteArray, seq: Int = -1) { val to = createToServiceMsg(cmd) val oidb = TrpcOidb( cmd = cmdId, service = serviceId, buffer = buffer, flag = 1 ) to.putWupBuffer(oidb.toByteArray()) to.addAttribute("req_pb_protocol_flag", true) if (seq != -1) { to.addAttribute("shamrock_seq", seq) } app.sendToService(to) } fun sendBuffer(cmd: String, isPb: Boolean, buffer: ByteArray, seq: Int = MsfService.getCore().nextSeq) { val toServiceMsg = ToServiceMsg("mobileqq.service", app.currentUin, cmd) toServiceMsg.putWupBuffer(buffer) toServiceMsg.addAttribute("req_pb_protocol_flag", isPb) toServiceMsg.addAttribute("shamrock_seq", seq) app.sendToService(toServiceMsg) } @OptIn(ExperimentalCoroutinesApi::class) override val coroutineContext: CoroutineContext by lazy { Dispatchers.IO.limitedParallelism(12) } } protected fun send(toServiceMsg: ToServiceMsg) { app.sendToService(toServiceMsg) } protected suspend fun sendAW(toServiceMsg: ToServiceMsg, timeout: Long = 5000L): ByteArray? { val seq = MsfService.getCore().nextSeq val buffer = withTimeoutOrNull(timeout) { suspendCancellableCoroutine { continuation -> launch(Dispatchers.Default) { DynamicReceiver.register(IPCRequest(toServiceMsg.serviceCmd, seq) { val buffer = it.getByteArrayExtra("buffer")!! continuation.resume(buffer) }) toServiceMsg.addAttribute("shamrock_seq", seq) send(toServiceMsg) } } }.also { if (it == null) DynamicReceiver.unregister(seq) }?.copyOf() try { if (buffer != null && buffer.size >= 5 && buffer[4] == 120.toByte()) { val builder = BytePacketBuilder() val deBuffer = DeflateTools.uncompress(buffer.slice(4)) builder.writeInt(deBuffer.size) builder.writeFully(deBuffer) return builder.build().readBytes() } } catch (_: Exception) { } return buffer } protected fun sendExtra(cmd: String, builder: (Bundle) -> Unit) { val toServiceMsg = createToServiceMsg(cmd) builder(toServiceMsg.extraData) app.sendToService(toServiceMsg) } protected fun sendPb(cmd: String, buffer: ByteArray, seq: Int) { val toServiceMsg = createToServiceMsg(cmd) toServiceMsg.putWupBuffer(buffer) toServiceMsg.addAttribute("req_pb_protocol_flag", true) toServiceMsg.addAttribute("shamrock_seq", seq) app.sendToService(toServiceMsg) } }