From b9ef0ca8f5c66ee3f1e8157df998ea812f6cb775 Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Tue, 21 Nov 2023 11:55:14 +0800 Subject: [PATCH 1/7] `Shamrock`: fix #67 --- .../moe/fuqiuluo/shamrock/remote/service/WebSocketService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/WebSocketService.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/WebSocketService.kt index 873dbf1..fe591a5 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/WebSocketService.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/WebSocketService.kt @@ -69,8 +69,8 @@ internal class WebSocketService(host: String, port: Int): WebSocketTransmitServl } val path = URI.create(handshake.resourceDescriptor).path if (path != "/api") { - pushMetaLifecycle() eventReceivers.add(conn) + pushMetaLifecycle() } LogCenter.log({ "WSServer连接(${conn.remoteSocketAddress.address.hostAddress}:${conn.remoteSocketAddress.port}$path)" }, Level.WARN) } From 2c49b107726d365f33207deebc43a7a87274e8ad Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Tue, 21 Nov 2023 11:58:32 +0800 Subject: [PATCH 2/7] `Shamrock`: fix #66 --- .../src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt index 9a1fad8..c784070 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt @@ -170,12 +170,12 @@ suspend fun PipelineContext.fetchPostOrThrow(key: String) } fun PipelineContext.isJsonData(): Boolean { - return ContentType.Application.Json == call.request.contentType() || call.attributes[isJsonKey] + return ContentType.Application.Json == call.request.contentType() || (isJsonKey in call.attributes && call.attributes[isJsonKey]) } suspend fun PipelineContext.isJsonString(key: String): Boolean { if (!isJsonData()) return true - val data = if (call.attributes.contains(jsonKey)) { + val data = if (jsonKey in call.attributes) { call.attributes[jsonKey] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { From 8265f74ca3a8911afd96dfd7ba7444864bcec436 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B0=8F=E6=A5=A0?= Date: Tue, 21 Nov 2023 18:15:54 +0800 Subject: [PATCH 3/7] =?UTF-8?q?=E8=A7=86=E9=A2=91=E7=B1=BB=E5=9E=8B?= =?UTF-8?q?=E6=B6=88=E6=81=AF=EF=BC=8Cmd5=E6=96=87=E4=BB=B6=E5=90=8D?= =?UTF-8?q?=E6=90=BA=E5=B8=A6=E5=90=8E=E7=BC=80=E5=AF=BC=E8=87=B4hex2bytes?= =?UTF-8?q?=E5=A4=B1=E8=B4=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../qqinterface/servlet/msg/convert/MessageElemConverter.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt index 9cffb1a..dda28d8 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/msg/convert/MessageElemConverter.kt @@ -143,13 +143,15 @@ internal sealed class MessageElemConverter: IMessageConvert { element: MsgElement ): MessageSegment { val video = element.videoElement + val md5 = video.fileName.split(".")[0] + return MessageSegment( type = "video", data = hashMapOf( "file" to video.fileName, "url" to when(chatType) { - MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", video.fileName, video.fileUuid) - MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", video.fileName, video.fileUuid) + MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) + MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid) else -> unknownChatType(chatType) } ).also { From fcf18bd3fdf243bead65254097bd585508f4e8e9 Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Tue, 21 Nov 2023 21:09:07 +0800 Subject: [PATCH 4/7] `Shamrock`: fix #68 --- .../fuqiuluo/shamrock/remote/api/MainRoute.kt | 4 +- .../service/api/WebSocketTransmitServlet.kt | 2 +- .../moe/fuqiuluo/shamrock/tools/KtorServer.kt | 93 ++++++++++--------- 3 files changed, 55 insertions(+), 44 deletions(-) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/MainRoute.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/MainRoute.kt index bacec9f..0e0a3c4 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/MainRoute.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/api/MainRoute.kt @@ -17,10 +17,12 @@ import moe.fuqiuluo.shamrock.remote.config.ECHO_KEY import moe.fuqiuluo.shamrock.remote.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.IndexData import moe.fuqiuluo.shamrock.remote.entries.Status +import moe.fuqiuluo.shamrock.tools.EmptyJsonObject import moe.fuqiuluo.shamrock.tools.fetchOrNull import moe.fuqiuluo.shamrock.tools.fetchOrThrow import moe.fuqiuluo.shamrock.tools.fetchPostJsonElement import moe.fuqiuluo.shamrock.tools.fetchPostJsonObject +import moe.fuqiuluo.shamrock.tools.fetchPostJsonObjectOrNull import moe.fuqiuluo.shamrock.tools.isJsonArray import moe.fuqiuluo.shamrock.tools.isJsonObject import moe.fuqiuluo.shamrock.tools.isJsonString @@ -55,7 +57,7 @@ fun Routing.echoVersion() { } call.attributes.put(ECHO_KEY, echo) - val params = fetchPostJsonObject("params") + val params = fetchPostJsonObjectOrNull("params") ?: EmptyJsonObject val handler = ActionManager[action] if (handler == null) { diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/WebSocketTransmitServlet.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/WebSocketTransmitServlet.kt index 44c0ffb..9377797 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/WebSocketTransmitServlet.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/api/WebSocketTransmitServlet.kt @@ -97,7 +97,7 @@ internal abstract class WebSocketTransmitServlet( } val action = actionObject["action"].asString val echo = actionObject["echo"] ?: EmptyJsonString - val params = actionObject["params"].asJsonObject + val params = actionObject["params"].asJsonObjectOrNull ?: EmptyJsonObject val handler = ActionManager[action] handler?.handle(ActionSession(params, echo)) diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt index c784070..625ef4c 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/tools/KtorServer.kt @@ -20,13 +20,10 @@ import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.jsonObject import moe.fuqiuluo.shamrock.helper.ParamsException import io.ktor.http.HttpMethod -import io.ktor.http.decodeURLPart import io.ktor.http.parseUrlEncodedParameters import io.ktor.server.request.httpMethod import io.ktor.server.routing.route import kotlinx.serialization.json.JsonElement -import moe.fuqiuluo.shamrock.helper.Level -import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.remote.entries.CommonResult import moe.fuqiuluo.shamrock.remote.entries.EmptyObject import moe.fuqiuluo.shamrock.remote.entries.Status @@ -38,9 +35,9 @@ import moe.fuqiuluo.shamrock.remote.entries.Status annotation class ShamrockDsl -private val isJsonKey = AttributeKey("isJson") -private val jsonKey = AttributeKey("paramsJson") -private val partsKey = AttributeKey("paramsParts") +private val keyIsJson = AttributeKey("isJson") +private val keyJsonObject = AttributeKey("paramsJson") +private val keyParts = AttributeKey("paramsParts") suspend fun ApplicationCall.fetch(key: String): String { val isPost = request.httpMethod == HttpMethod.Post @@ -94,23 +91,23 @@ fun ApplicationCall.isJsonData(): Boolean { } suspend fun ApplicationCall.fetchPostOrNull(key: String): String? { - if (attributes.contains(jsonKey)) { - return attributes[jsonKey][key].asStringOrNull + if (attributes.contains(keyJsonObject)) { + return attributes[keyJsonObject][key].asStringOrNull } - if (attributes.contains(partsKey)) { - return attributes[partsKey][key] + if (attributes.contains(keyParts)) { + return attributes[keyParts][key] } return kotlin.runCatching { if (isJsonData()) { Json.parseToJsonElement(receiveText()).jsonObject.also { - attributes.put(jsonKey, it) - attributes.put(isJsonKey, true) + attributes.put(keyJsonObject, it) + attributes.put(keyIsJson, true) }[key].asStringOrNull } else if ( ContentType.Application.FormUrlEncoded == request.contentType() ) { receiveParameters().also { - attributes.put(partsKey, it) + attributes.put(keyParts, it) }[key] } else { receiveTextAsUnknown(key) @@ -124,13 +121,13 @@ private suspend fun ApplicationCall.receiveTextAsUnknown(key: String): String? { return receiveText().let { text -> if (text.startsWith("{") && text.endsWith("}")) { Json.parseToJsonElement(text).jsonObject.also { - attributes.put(jsonKey, it) - attributes.put(isJsonKey, true) + attributes.put(keyJsonObject, it) + attributes.put(keyIsJson, true) }[key].asStringOrNull } else { text.parseUrlEncodedParameters().also { - attributes.put(partsKey, it) - attributes.put(isJsonKey, false) + attributes.put(keyParts, it) + attributes.put(keyIsJson, false) }[key] } } // receiveText @@ -170,17 +167,17 @@ suspend fun PipelineContext.fetchPostOrThrow(key: String) } fun PipelineContext.isJsonData(): Boolean { - return ContentType.Application.Json == call.request.contentType() || (isJsonKey in call.attributes && call.attributes[isJsonKey]) + return ContentType.Application.Json == call.request.contentType() || (keyIsJson in call.attributes && call.attributes[keyIsJson]) } suspend fun PipelineContext.isJsonString(key: String): Boolean { if (!isJsonData()) return true - val data = if (jsonKey in call.attributes) { - call.attributes[jsonKey] + val data = if (keyJsonObject in call.attributes) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) - call.attributes.put(isJsonKey, true) + call.attributes.put(keyJsonObject, it) + call.attributes.put(keyIsJson, true) } } return data[key] is JsonPrimitive @@ -188,11 +185,11 @@ suspend fun PipelineContext.isJsonString(key: String): Bo suspend fun PipelineContext.isJsonObject(key: String): Boolean { if (!isJsonData()) return false - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) + call.attributes.put(keyJsonObject, it) } } return data[key] is JsonObject @@ -200,56 +197,68 @@ suspend fun PipelineContext.isJsonObject(key: String): Bo suspend fun PipelineContext.isJsonArray(key: String): Boolean { if (!isJsonData()) return false - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) + call.attributes.put(keyJsonObject, it) } } return data[key] is JsonArray } suspend fun PipelineContext.fetchPostJsonString(key: String): String { - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) + call.attributes.put(keyJsonObject, it) } } return data[key].asString } suspend fun PipelineContext.fetchPostJsonElement(key: String): JsonElement { - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) + call.attributes.put(keyJsonObject, it) } } return data[key]!! } suspend fun PipelineContext.fetchPostJsonObject(key: String): JsonObject { - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) + call.attributes.put(keyJsonObject, it) } } return data[key].asJsonObject } -suspend fun PipelineContext.fetchPostJsonArray(key: String): JsonArray { - val data = if (call.attributes.contains(jsonKey)) { - call.attributes[jsonKey] +suspend fun PipelineContext.fetchPostJsonObjectOrNull(key: String): JsonObject? { + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] } else { Json.parseToJsonElement(call.receiveText()).jsonObject.also { - call.attributes.put(jsonKey, it) - call.attributes.put(isJsonKey, true) + call.attributes.put(keyJsonObject, it) + } + } + return data[key].asJsonObjectOrNull +} + + +suspend fun PipelineContext.fetchPostJsonArray(key: String): JsonArray { + val data = if (call.attributes.contains(keyJsonObject)) { + call.attributes[keyJsonObject] + } else { + Json.parseToJsonElement(call.receiveText()).jsonObject.also { + call.attributes.put(keyJsonObject, it) + call.attributes.put(keyIsJson, true) } } return data[key].asJsonArray From 2af8d6d81711edc6f0072a219b614585333a453b Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Tue, 21 Nov 2023 22:05:34 +0800 Subject: [PATCH 5/7] `Shamrock`: Gwellekaat dilemel ar restr strollad --- app/build.gradle.kts | 5 +++- .../GroupFileCommonResult.java | 4 --- .../IDeleteGroupFileCallback.java | 2 +- .../IKernelRichMediaService.java | 2 +- .../fuqiuluo/qqinterface/servlet/FileSvc.kt | 25 ++++++++++++++++++- .../remote/action/handlers/DeleteGroupFile.kt | 20 +++++++++++++++ .../service/listener/PrimitiveListener.kt | 2 +- 7 files changed, 51 insertions(+), 9 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 7d342a7..d0443c5 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -24,7 +24,7 @@ android { minSdk = 24 targetSdk = 33 versionCode = (System.currentTimeMillis() / 1000).toInt() - versionName = "1.0.5-dev" + gitCommitHash() + versionName = "1.0.6-dev" + gitCommitHash() testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -66,6 +66,7 @@ android { create("app") { dimension = "mode" ndk { + println("Full architecture and full compilation.") abiFilters.add("arm64-v8a") abiFilters.add("x86_64") } @@ -73,12 +74,14 @@ android { create("arm64") { dimension = "mode" ndk { + println("Full compilation of arm64 architecture") abiFilters.add("arm64-v8a") } } create("x64") { dimension = "mode" ndk { + println("Full compilation of x64 architecture") abiFilters.add("x86_64") } } diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GroupFileCommonResult.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GroupFileCommonResult.java index b449cf6..b5567a3 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GroupFileCommonResult.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/GroupFileCommonResult.java @@ -1,7 +1,5 @@ package com.tencent.qqnt.kernel.nativeinterface; -/* compiled from: P */ -/* loaded from: classes2.dex */ public final class GroupFileCommonResult { String clientWording; int retCode; @@ -29,8 +27,6 @@ public final class GroupFileCommonResult { } public GroupFileCommonResult(int i2, String str, String str2) { - this.retMsg = ""; - this.clientWording = ""; this.retCode = i2; this.retMsg = str; this.clientWording = str2; diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IDeleteGroupFileCallback.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IDeleteGroupFileCallback.java index 5fefb93..210f3ba 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IDeleteGroupFileCallback.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IDeleteGroupFileCallback.java @@ -1,5 +1,5 @@ package com.tencent.qqnt.kernel.nativeinterface; public interface IDeleteGroupFileCallback { - void onResult(int i2, String str, DeleteGroupFileResult deleteGroupFileResult); + void onResult(int code, String why, DeleteGroupFileResult result); } \ No newline at end of file diff --git a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelRichMediaService.java b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelRichMediaService.java index b43759c..7201af5 100644 --- a/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelRichMediaService.java +++ b/qqinterface/src/main/java/com/tencent/qqnt/kernel/nativeinterface/IKernelRichMediaService.java @@ -9,7 +9,7 @@ public interface IKernelRichMediaService { void cancelTransferTask(Contact contact, ArrayList arrayList, ArrayList arrayList2, IOperateTransferInfoCallback iOperateTransferInfoCallback); - void deleteGroupFile(long j2, String str, int i2, IDeleteGroupFileCallback iDeleteGroupFileCallback); + void deleteGroupFile(long groupCode, String fileUid, int bizId, IDeleteGroupFileCallback cb); void deleteTransferInfo(Contact contact, ArrayList arrayList, IOperateTransferInfoCallback iOperateTransferInfoCallback); diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt index d44be64..3d4067e 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/FileSvc.kt @@ -1,7 +1,12 @@ package moe.fuqiuluo.qqinterface.servlet import com.tencent.mobileqq.pb.ByteStringMicro +import com.tencent.qqnt.kernel.nativeinterface.DeleteGroupFileResult +import com.tencent.qqnt.kernel.nativeinterface.GroupFileCommonResult +import com.tencent.qqnt.kernel.nativeinterface.IDeleteGroupFileCallback import io.ktor.util.Deflate +import kotlinx.coroutines.suspendCancellableCoroutine +import kotlinx.coroutines.withTimeoutOrNull import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import moe.fuqiuluo.proto.protobufOf @@ -12,8 +17,11 @@ import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY import moe.fuqiuluo.shamrock.tools.slice import moe.fuqiuluo.shamrock.tools.toHexString import moe.fuqiuluo.shamrock.utils.DeflateTools +import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher import tencent.im.oidb.cmd0x6d8.oidb_0x6d8 import tencent.im.oidb.oidb_sso +import kotlin.coroutines.resume +import kotlin.coroutines.suspendCoroutine internal object FileSvc: BaseSvc() { fun createFileFolder(groupId: String, folderName: String) { @@ -38,8 +46,23 @@ internal object FileSvc: BaseSvc() { } fun deleteGroupFile(groupId: String, bizId: Int, fileUid: String) { + /* + val kernelService = NTServiceFetcher.kernelService + val sessionService = kernelService.wrapperSession + val richMediaService = sessionService.richMediaService + + val result = withTimeoutOrNull(3000L) { + suspendCancellableCoroutine { + richMediaService.deleteGroupFile(groupId.toLong(), fileUid, bizId) { code, _, result -> + it.resume(code to result.result) + } + } + } + + return if (result == null) Result.failure(RuntimeException("delete group file timeout")) else Result.success(result)*/ + // 调用QQ内部实现会导致闪退! sendOidb("OidbSvc.0x6d6_3", 1750, 3, protobufOf( - 4 to mapOf( + 4 to mapOf( 1 to groupId.toLong(), 2 to 3, 3 to bizId, diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/DeleteGroupFile.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/DeleteGroupFile.kt index febc738..f2d2938 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/DeleteGroupFile.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/action/handlers/DeleteGroupFile.kt @@ -14,10 +14,30 @@ internal object DeleteGroupFile: IActionHandler() { return invoke(groupId, fileId, busid, session.echo) } + /* + suspend operator fun invoke( + groupId: String, + fileId: String, + bizId: Int, + echo: JsonElement = EmptyJsonString + ): String { + val result = FileSvc.deleteGroupFile(groupId, bizId, fileId) + if(result.isFailure) { + return error(result.exceptionOrNull()?.message ?: "删除群文件失败", echo) + } + val commonResult = result.getOrThrow() + if (commonResult.first != 0 || commonResult.second.retCode != 0) { + return error(commonResult.second.clientWording, echo) + } + return ok("成功", echo) + } + */ operator fun invoke(groupId: String, fileId: String, bizId: Int, echo: JsonElement = EmptyJsonString): String { FileSvc.deleteGroupFile(groupId, bizId, fileId) return ok("成功", echo) } + override val requiredParams: Array = arrayOf("group_id", "file_id", "busid") + override fun path(): String = "delete_group_file" } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/PrimitiveListener.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/PrimitiveListener.kt index c51257a..e7f8dee 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/PrimitiveListener.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/remote/service/listener/PrimitiveListener.kt @@ -52,7 +52,7 @@ internal object PrimitiveListener { ) return val msgType = pb[1, 2, 1].asInt var subType = 0 - if (pb.has(1, 2, 3)) { + if (pb.has(1, 2, 3) && pb.has(1, 2, 2)) { subType = pb[1, 2, 2].asInt } val msgTime = pb[1, 2, 6].asLong From 98c6316e23cecb128c301a5331eaec906105c7b0 Mon Sep 17 00:00:00 2001 From: WhiteChi Date: Tue, 21 Nov 2023 22:30:53 +0800 Subject: [PATCH 6/7] `Shamrock`: fix #61 --- .../fuqiuluo/qqinterface/servlet/BaseSvc.kt | 15 ++++++++++---- .../servlet/transfile/RichProtoSvc.kt | 20 ++++++++++++------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/BaseSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/BaseSvc.kt index 03bf15d..6efa17a 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/BaseSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/BaseSvc.kt @@ -11,6 +11,7 @@ import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.proto.protobufOf import moe.fuqiuluo.shamrock.utils.PlatformUtils @@ -37,9 +38,9 @@ internal abstract class BaseSvc { } suspend fun sendOidbAW(cmd: String, cmdId: Int, serviceId: Int, data: ByteArray, trpc: Boolean = false, timeout: Long = 5000L): ByteArray? { + val seq = MsfCore.getNextSeq() return withTimeoutOrNull(timeout) { - suspendCoroutine { continuation -> - val seq = MsfCore.getNextSeq() + suspendCancellableCoroutine { continuation -> GlobalScope.launch(Dispatchers.Default) { DynamicReceiver.register(IPCRequest(cmd, seq) { val buffer = it.getByteArrayExtra("buffer")!! @@ -49,13 +50,16 @@ internal abstract class BaseSvc { if (trpc) sendTrpcOidb(cmd, cmdId, serviceId, data, seq) else sendOidb(cmd, cmdId, serviceId, data, seq) } + }.also { + if (it == null) + DynamicReceiver.unregister(seq) }?.copyOf() } suspend fun sendBufferAW(cmd: String, isPb: Boolean, data: ByteArray, timeout: Long = 5000L): ByteArray? { + val seq = MsfCore.getNextSeq() return withTimeoutOrNull(timeout) { - suspendCoroutine { continuation -> - val seq = MsfCore.getNextSeq() + suspendCancellableCoroutine { continuation -> GlobalScope.launch(Dispatchers.Default) { DynamicReceiver.register(IPCRequest(cmd, seq) { val buffer = it.getByteArrayExtra("buffer")!! @@ -64,6 +68,9 @@ internal abstract class BaseSvc { sendBuffer(cmd, isPb, data, seq) } } + }.also { + if (it == null) + DynamicReceiver.unregister(seq) }?.copyOf() } diff --git a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt index 28cc774..50f72cc 100644 --- a/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt +++ b/xposed/src/main/java/moe/fuqiuluo/qqinterface/servlet/transfile/RichProtoSvc.kt @@ -63,24 +63,30 @@ internal object RichProtoSvc: BaseSvc() { suspend fun getC2CFileDownUrl( fileId: String, subId: String, + retryCnt: Int = 0 ): String { - val uid = ContactHelper.getUidByUinAsync(app.currentUin.toLong()) - val buffer = sendOidbAW("OidbSvcTrpcTcp.0xe37_1200", 3639, 1200, protobufOf( + val buffer = sendOidbAW("OidbSvc.0xe37_1200", 3639, 1200, protobufOf( 1 to 1200, 2 to 1 /* QRoute.api(IAudioHelperApi::class.java).genDebugSeq().toInt() */, /* seq */ 14 to mapOf( - 10 to uid, + 10 to app.longAccountUin, 20 to fileId, 30 to 2, /* ver */ 60 to subId, 601 to 0 ), - 101 to 3, + 101 to 3, // uint32_business_id 102 to 104, /* client_type */ - 200 to 1, /* url_type */ - 99999 to 90200 to 1 - ).toByteArray(), trpc = true) + 200 to 1, /* uint32_flag_support_mediaplatform */ + 99999 to mapOf( + 90200 to 1 // uint32_download_url_type + ) + ).toByteArray()) + if (buffer == null) { + if (retryCnt < 3) { + return getC2CFileDownUrl(fileId, subId, retryCnt + 1) + } return "" } else { val body = oidb_sso.OIDBSSOPkg() From c87268df0926be6340a4d9ed957628a84d587da6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B1=A0?= <98259561+whitechi73@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:47:38 +0800 Subject: [PATCH 7/7] Create FUNDING.yml --- .github/FUNDING.yml | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..f527e93 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,13 @@ +# These are supported funding model platforms + +github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: shamrock320 # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']