From cb4268edef7761ea9063604dc86d125d719d02d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B1=A0?= Date: Mon, 11 Mar 2024 12:48:11 +0800 Subject: [PATCH] =?UTF-8?q?`Shamrock`:=20=E5=AE=9E=E7=8E=B0Kritor=E6=A0=B8?= =?UTF-8?q?=E5=BF=83=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 白池 --- .../main/java/kritor/server/KritorServer.kt | 1 + .../main/java/kritor/service/KritorService.kt | 118 ++++++++++++++++++ .../fuqiuluo/shamrock/utils/DownloadUtils.kt | 2 +- .../service/contact/ProfileProtocolConst.kt | 39 ------ 4 files changed, 120 insertions(+), 40 deletions(-) create mode 100644 xposed/src/main/java/kritor/service/KritorService.kt delete mode 100644 xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt diff --git a/xposed/src/main/java/kritor/server/KritorServer.kt b/xposed/src/main/java/kritor/server/KritorServer.kt index 376f80b..df27d30 100644 --- a/xposed/src/main/java/kritor/server/KritorServer.kt +++ b/xposed/src/main/java/kritor/server/KritorServer.kt @@ -20,6 +20,7 @@ class KritorServer( .intercept(AuthInterceptor) .addService(Authentication) .addService(ContactService) + .addService(KritorService) .build()!! fun start(block: Boolean = false) { diff --git a/xposed/src/main/java/kritor/service/KritorService.kt b/xposed/src/main/java/kritor/service/KritorService.kt new file mode 100644 index 0000000..e53387b --- /dev/null +++ b/xposed/src/main/java/kritor/service/KritorService.kt @@ -0,0 +1,118 @@ +package kritor.service + +import android.util.Base64 +import com.tencent.mobileqq.app.QQAppInterface +import io.grpc.Status +import io.grpc.StatusRuntimeException +import io.kritor.core.ClearCacheRequest +import io.kritor.core.ClearCacheResponse +import io.kritor.core.DownloadFileRequest +import io.kritor.core.DownloadFileResponse +import io.kritor.core.GetCurrentAccountRequest +import io.kritor.core.GetCurrentAccountResponse +import io.kritor.core.GetVersionRequest +import io.kritor.core.GetVersionResponse +import io.kritor.core.KritorServiceGrpcKt +import io.kritor.core.SwitchAccountRequest +import io.kritor.core.SwitchAccountResponse +import io.kritor.core.clearCacheResponse +import io.kritor.core.downloadFileResponse +import io.kritor.core.getCurrentAccountResponse +import io.kritor.core.getVersionResponse +import io.kritor.core.switchAccountResponse +import moe.fuqiuluo.shamrock.tools.ShamrockVersion +import moe.fuqiuluo.shamrock.utils.DownloadUtils +import moe.fuqiuluo.shamrock.utils.FileUtils +import moe.fuqiuluo.shamrock.utils.MD5 +import moe.fuqiuluo.shamrock.utils.MMKVFetcher +import mqq.app.MobileQQ +import qq.service.QQInterfaces +import qq.service.QQInterfaces.Companion.app +import qq.service.contact.ContactHelper +import java.io.File + +object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBase() { + @Grpc("KritorService", "GetVersion") + override suspend fun getVersion(request: GetVersionRequest): GetVersionResponse { + return getVersionResponse { + this.version = ShamrockVersion + this.appName = "Shamrock" + } + } + + @Grpc("KritorService", "ClearCache") + override suspend fun clearCache(request: ClearCacheRequest): ClearCacheResponse { + FileUtils.clearCache() + MMKVFetcher.mmkvWithId("audio2silk") + .clear() + return clearCacheResponse {} + } + + @Grpc("KritorService", "GetCurrentAccount") + override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse { + return getCurrentAccountResponse { + this.accountName = if (app is QQAppInterface) app.currentNickname else "unknown" + this.accountUid = app.currentUid ?: "" + this.accountUin = (app.currentUin ?: "0").toLong() + } + } + + @Grpc("KritorService", "DownloadFile") + override suspend fun downloadFile(request: DownloadFileRequest): DownloadFileResponse { + val headerMap = mutableMapOf( + "User-Agent" to "Shamrock" + ) + if (request.hasHeaders()) { + request.headers.split("[\r\n]").forEach { + val pair = it.split("=") + if (pair.size >= 2) { + val (k, v) = pair + headerMap[k] = v + } + } + } + + var tmp = FileUtils.getTmpFile("cache") + if (request.hasBase64()) { + val bytes = Base64.decode(request.base64, Base64.DEFAULT) + tmp.writeBytes(bytes) + } else if(request.hasUrl()) { + if(!DownloadUtils.download( + urlAdr = request.url, + dest = tmp, + headers = headerMap, + threadCount = if (request.hasThreadCnt()) request.threadCnt else 3 + )) { + throw StatusRuntimeException(Status.INTERNAL.withDescription("download failed")) + } + } + tmp = if (!request.hasFileName()) FileUtils.renameByMd5(tmp) + else tmp.parentFile!!.resolve(request.fileName).also { + tmp.renameTo(it) + } + if (request.hasRootPath()) { + tmp = File(request.rootPath).resolve(tmp.name).also { + tmp.renameTo(it) + } + } + + return downloadFileResponse { + this.fileMd5 = MD5.genFileMd5Hex(tmp.absolutePath) + this.fileAbsolutePath = tmp.absolutePath + } + } + + @Grpc("KritorService", "SwitchAccount") + override suspend fun switchAccount(request: SwitchAccountRequest): SwitchAccountResponse { + val uin = if (request.hasAccountUin()) request.accountUin.toString() + else ContactHelper.getUinByUidAsync(request.accountUid) + val account = MobileQQ.getMobileQQ().allAccounts.firstOrNull { it.uin == uin } + ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("account not found")) + runCatching { + app.switchAccount(account, null) + }.onFailure { + throw StatusRuntimeException(Status.INTERNAL.withCause(it).withDescription("failed to switch account")) + } + return switchAccountResponse { } + } +} \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DownloadUtils.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DownloadUtils.kt index 1cdaf5a..73d5ed7 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DownloadUtils.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/utils/DownloadUtils.kt @@ -33,7 +33,7 @@ object DownloadUtils { threadCount: Int = MAX_THREAD, headers: Map = mapOf() ): Boolean { - var threadCnt = if(threadCount == 0) MAX_THREAD else threadCount + var threadCnt = if(threadCount == 0 || threadCount < 0) MAX_THREAD else threadCount val url = URL(urlAdr) val connection = withContext(Dispatchers.IO) { url.openConnection() } as HttpURLConnection headers.forEach { (k, v) -> diff --git a/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt b/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt deleted file mode 100644 index cf486c0..0000000 --- a/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt +++ /dev/null @@ -1,39 +0,0 @@ -package qq.service.contact - -const val CMD_GET_PROFILE_DETAIL = "OidbSvc.0x480_9_IMCore" -const val CMD_SET_PROFILE_DETAIL = "OidbSvc.0x4ff_9_IMCore" -const val KET_INTERESTS = "interests" -const val KEY_AGE = "age" -const val KEY_BIRTHDAY = "birthday" -const val KEY_COLLEGE = "college" -const val KEY_COMPANY = "company" -const val KEY_CONSTELLATION = "key_constellation" -const val KEY_EMAIL = "email" -const val KEY_HOMETOWN = "hometown" -const val KEY_HOMETOWN_DESC = "hometown_desc" -const val KEY_LOCATION = "location" -const val KEY_LOCATION_DESC = "location_desc" -const val KEY_LOCATION_NAME = "location_name" -const val KEY_NICK = "nick" -const val KEY_PARSE_PROFILE_LOCATION = "parse_profile_location" -const val KEY_PERSONAL_NOTE = "personalNote" -const val KEY_PROFESSION = "profession" -const val KEY_SEX = "sex" -const val PARAM_ADD_FRIEND_SOURCE = "addFriendSource" -const val PARAM_COME_FROM_TYPE = "comeFromType" -const val PARAM_GET_CONTROL = "getControl" -const val PARAM_IS_FRIEND = "isFriend" -const val PARAM_LOGIN_SIG = "loginSig" -const val PARAM_QZONE_FEED_TIMESTAMP = "qZoneFeedTimeStamp" -const val PARAM_QZONE_SEED = "qZoneSeed" -const val PARAM_REQ_0X5EB = "req0x5ebList" -const val PARAM_REQ_EXTEND = "reqExtendFriend" -const val PARAM_REQ_MEDAL = "reqMedalWall" -const val PARAM_REQ_SERVICES = "reqServiceList" -const val PARAM_REQ_TEMPLATE = "reqTemplate" -const val PARAM_SEARCH_NAME = "searchName" -const val PARAM_SECURE_SIG = "secureSig" -const val PARAM_SELF_UIN = "selfUin" -const val PARAM_TARGET_UIN = "targetUin" -const val PARAM_TROOP_CODE = "troopCode" -const val PARAM_TROOP_UIN = "troopUin" \ No newline at end of file