diff --git a/.gitmodules b/.gitmodules index c8727557..05db34d9 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "kritor"] - path = kritor - url = https://github.com/KarinJS/kritor-kotlin + path = kritor/kritor + url = https://github.com/KarinJS/kritor diff --git a/annotations/src/main/java/kritor/service/Grpc.kt b/annotations/src/main/java/kritor/service/Grpc.kt index 2d6fe33d..e3f67c8d 100644 --- a/annotations/src/main/java/kritor/service/Grpc.kt +++ b/annotations/src/main/java/kritor/service/Grpc.kt @@ -1,7 +1,5 @@ package kritor.service -import kotlin.reflect.KClass - @Retention(AnnotationRetention.SOURCE) @Target(AnnotationTarget.FUNCTION) annotation class Grpc( diff --git a/annotations/src/main/java/moe/fuqiuluo/symbols/OneBotHandler.kt b/annotations/src/main/java/moe/fuqiuluo/symbols/OneBotHandler.kt deleted file mode 100644 index d8326dfe..00000000 --- a/annotations/src/main/java/moe/fuqiuluo/symbols/OneBotHandler.kt +++ /dev/null @@ -1,8 +0,0 @@ -package moe.fuqiuluo.symbols - -@Retention(AnnotationRetention.SOURCE) -@Target(AnnotationTarget.CLASS) -annotation class OneBotHandler( - val actionName: String, - val alias: Array = [] -) diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts index b2cd5210..fb7efa55 100644 --- a/buildSrc/build.gradle.kts +++ b/buildSrc/build.gradle.kts @@ -1,5 +1,5 @@ plugins { - kotlin("jvm") version "1.9.21" + kotlin("jvm") version "1.9.22" } repositories { diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index b2897283..20deb65e 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -15,8 +15,9 @@ fun ktor(target: String, name: String): String { return "io.ktor:ktor-$target-$name:${Versions.ktorVersion}" } +fun grpc(name: String, version: String) = "io.grpc:grpc-$name:$version" + object Versions { const val roomVersion = "2.5.0" - const val ktorVersion = "2.3.3" } \ No newline at end of file diff --git a/kritor b/kritor deleted file mode 160000 index 10dca646..00000000 --- a/kritor +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 10dca646c83d1ef45deab0ac4ab2d80446e902cf diff --git a/kritor/.gitignore b/kritor/.gitignore new file mode 100644 index 00000000..b63da455 --- /dev/null +++ b/kritor/.gitignore @@ -0,0 +1,42 @@ +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/kritor/build.gradle.kts b/kritor/build.gradle.kts new file mode 100644 index 00000000..f9b75f35 --- /dev/null +++ b/kritor/build.gradle.kts @@ -0,0 +1,75 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + id("com.android.library") + id("org.jetbrains.kotlin.android") + id("com.google.protobuf") version "0.9.4" +} + +android { + namespace = "moe.whitechi73.kritor" + compileSdk = 34 + + defaultConfig { + minSdk = 24 + + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + isMinifyEnabled = false + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } +} + +dependencies { + protobuf(files("kritor/protos")) + + implementation(kotlinx("coroutines-core", "1.8.0")) + implementation("com.google.protobuf:protobuf-java:3.25.3") + + implementation(grpc("stub", "1.62.2")) + implementation(grpc("kotlin-stub", "1.4.1")) + implementation(grpc("protobuf", "1.62.2")) +} + +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.25.3" + } + plugins { + create("grpc") { + artifact = "io.grpc:protoc-gen-grpc-java:1.62.2" + } + create("grpckt") { + artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar" + } + } + generateProtoTasks { + all().forEach { + it.plugins { + create("grpc") + create("grpckt") + } + it.builtins { + create("java") + } + } + } +} + +tasks.withType().configureEach { + kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn" +} \ No newline at end of file diff --git a/kritor/consumer-rules.pro b/kritor/consumer-rules.pro new file mode 100644 index 00000000..e69de29b diff --git a/kritor/kritor b/kritor/kritor new file mode 160000 index 00000000..94f2e74b --- /dev/null +++ b/kritor/kritor @@ -0,0 +1 @@ +Subproject commit 94f2e74b79b003685f785336a279d2cc75f95829 diff --git a/kritor/proguard-rules.pro b/kritor/proguard-rules.pro new file mode 100644 index 00000000..481bb434 --- /dev/null +++ b/kritor/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/protobuf/build.gradle.kts b/protobuf/build.gradle.kts index db8dae52..0daa86ed 100644 --- a/protobuf/build.gradle.kts +++ b/protobuf/build.gradle.kts @@ -37,7 +37,6 @@ android { } dependencies { - //implementation(DEPENDENCY_PROTOBUF) implementation(kotlinx("serialization-protobuf", "1.6.2")) implementation(kotlinx("serialization-json", "1.6.2")) diff --git a/protobuf/src/main/java/protobuf/message/element/commelem/ButtonExtra.kt b/protobuf/src/main/java/protobuf/message/element/commelem/ButtonExtra.kt index 19b56adf..43c89968 100644 --- a/protobuf/src/main/java/protobuf/message/element/commelem/ButtonExtra.kt +++ b/protobuf/src/main/java/protobuf/message/element/commelem/ButtonExtra.kt @@ -13,7 +13,7 @@ data class ButtonExtra( @Serializable data class Object1( @ProtoNumber(1) val rows: List? = null, - @ProtoNumber(2) val appid: Int? = null, + @ProtoNumber(2) val appid: ULong? = null, ) @Serializable diff --git a/settings.gradle.kts b/settings.gradle.kts index e5f61308..78c185b2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -26,7 +26,7 @@ buildscript { } } dependencies { - classpath("com.android.tools:r8:8.2.47") + classpath("com.android.tools:r8:8.3.37") } } @@ -34,11 +34,9 @@ rootProject.name = "Shamrock" include( ":app", ":xposed", - ":qqinterface" -) -include(":protobuf") -include(":processor") -include(":annotations") -include(":kritor") - -project(":kritor").projectDir = file("kritor/protos") \ No newline at end of file + ":qqinterface", + ":protobuf", + ":processor", + ":annotations", + ":kritor" +) \ No newline at end of file diff --git a/xposed/build.gradle.kts b/xposed/build.gradle.kts index 8048a78d..6ce189d6 100644 --- a/xposed/build.gradle.kts +++ b/xposed/build.gradle.kts @@ -5,7 +5,6 @@ plugins { id("org.jetbrains.kotlin.android") id("kotlin-kapt") id("com.google.devtools.ksp") version "1.9.22-1.0.17" - id("com.google.protobuf") version "0.9.4" kotlin("plugin.serialization") version "1.9.22" } @@ -61,11 +60,10 @@ kotlin { } dependencies { - compileOnly ("de.robv.android.xposed:api:82") - compileOnly (project(":qqinterface")) - - protobuf(project(":kritor")) + compileOnly("de.robv.android.xposed:api:82") + compileOnly(project(":qqinterface")) + implementation(project(":kritor")) implementation(project(":protobuf")) implementation(project(":annotations")) ksp(project(":processor")) @@ -75,24 +73,20 @@ dependencies { DEPENDENCY_ANDROIDX.forEach { implementation(it) } - //implementation(DEPENDENCY_PROTOBUF) implementation(room("runtime")) kapt(room("compiler")) implementation(room("ktx")) implementation(kotlinx("io-jvm", "0.1.16")) - implementation(kotlinx("serialization-protobuf", "1.6.2")) implementation(ktor("client", "core")) implementation(ktor("client", "okhttp")) implementation(ktor("serialization", "kotlinx-json")) - implementation("io.grpc:grpc-stub:1.62.2") - implementation("io.grpc:grpc-protobuf-lite:1.62.2") - implementation("com.google.protobuf:protobuf-kotlin-lite:3.25.3") - implementation("io.grpc:grpc-kotlin-stub:1.4.1") - implementation("io.grpc:grpc-okhttp:1.62.2") + implementation(grpc("protobuf", "1.62.2")) + implementation(grpc("kotlin-stub", "1.4.1")) + implementation(grpc("okhttp", "1.62.2")) testImplementation("junit:junit:4.13.2") androidTestImplementation("androidx.test.ext:junit:1.1.5") @@ -106,40 +100,3 @@ tasks.withType().all { freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn") } } - -protobuf { - protoc { - artifact = "com.google.protobuf:protoc:3.25.3" - } - plugins { - create("java") { - artifact = "io.grpc:protoc-gen-grpc-java:1.62.2" - } - create("grpc") { - artifact = "io.grpc:protoc-gen-grpc-java:1.62.2" - } - create("grpckt") { - artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar" - } - } - generateProtoTasks { - all().forEach { - it.plugins { - create("java") { - option("lite") - } - create("grpc") { - option("lite") - } - create("grpckt") { - option("lite") - } - } - it.builtins { - create("kotlin") { - option("lite") - } - } - } - } -} diff --git a/xposed/src/main/java/kritor/client/KritorClient.kt b/xposed/src/main/java/kritor/client/KritorClient.kt index c31a0f11..2d7b4953 100644 --- a/xposed/src/main/java/kritor/client/KritorClient.kt +++ b/xposed/src/main/java/kritor/client/KritorClient.kt @@ -5,10 +5,7 @@ import com.google.protobuf.ByteString import io.grpc.ManagedChannel import io.grpc.ManagedChannelBuilder import io.kritor.ReverseServiceGrpcKt -import io.kritor.event.EventServiceGrpcKt -import io.kritor.event.EventType -import io.kritor.event.eventStructure -import io.kritor.event.messageEvent +import io.kritor.event.* import io.kritor.reverse.ReqCode import io.kritor.reverse.Request import io.kritor.reverse.Response @@ -83,23 +80,23 @@ internal class KritorClient( EventServiceGrpcKt.EventServiceCoroutineStub(channel).registerPassiveListener(channelFlow { when(eventType) { EventType.EVENT_TYPE_MESSAGE -> GlobalEventTransmitter.onMessageEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_MESSAGE this.message = it.second - }) + }.build()) } EventType.EVENT_TYPE_CORE_EVENT -> {} EventType.EVENT_TYPE_NOTICE -> GlobalEventTransmitter.onNoticeEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_NOTICE this.notice = it - }) + }.build()) } EventType.EVENT_TYPE_REQUEST -> GlobalEventTransmitter.onRequestEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_REQUEST this.request = it - }) + }.build()) } EventType.UNRECOGNIZED -> {} } diff --git a/xposed/src/main/java/kritor/service/Authentication.kt b/xposed/src/main/java/kritor/service/Authentication.kt index 20aac7ae..854bbb5f 100644 --- a/xposed/src/main/java/kritor/service/Authentication.kt +++ b/xposed/src/main/java/kritor/service/Authentication.kt @@ -8,8 +8,6 @@ import io.kritor.AuthRsp import io.kritor.AuthenticationGrpcKt import io.kritor.GetAuthStateReq import io.kritor.GetAuthStateRsp -import io.kritor.authRsp -import io.kritor.getAuthStateRsp import kritor.auth.AuthInterceptor import moe.fuqiuluo.shamrock.config.ActiveTicket import moe.fuqiuluo.shamrock.config.ShamrockConfig @@ -19,10 +17,10 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl @Grpc("Authentication", "Auth") override suspend fun auth(request: AuthReq): AuthRsp { if (QQInterfaces.app.account != request.account) { - return authRsp { + return AuthRsp.newBuilder().apply { code = AuthCode.NO_ACCOUNT msg = "No such account" - } + }.build() } val activeTicketName = ActiveTicket.name() @@ -31,26 +29,26 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl val ticket = ShamrockConfig.getProperty(activeTicketName + if (index == 0) "" else ".$index", null) if (ticket.isNullOrEmpty()) { if (index == 0) { - return authRsp { + return AuthRsp.newBuilder().apply { code = AuthCode.OK msg = "OK" - } + }.build() } else { break } } else if (ticket == request.ticket) { - return authRsp { + return AuthRsp.newBuilder().apply { code = AuthCode.OK msg = "OK" - } + }.build() } index++ } - return authRsp { + return AuthRsp.newBuilder().apply { code = AuthCode.NO_TICKET msg = "Invalid ticket" - } + }.build() } @Grpc("Authentication", "GetAuthState") @@ -59,8 +57,8 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl throw StatusRuntimeException(Status.CANCELLED.withDescription("No such account")) } - return getAuthStateRsp { + return GetAuthStateRsp.newBuilder().apply { isRequiredAuth = AuthInterceptor.getAllTicket().isNotEmpty() - } + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/ContactService.kt b/xposed/src/main/java/kritor/service/ContactService.kt index 8dc8de79..da2a2192 100644 --- a/xposed/src/main/java/kritor/service/ContactService.kt +++ b/xposed/src/main/java/kritor/service/ContactService.kt @@ -23,31 +23,31 @@ import io.kritor.contact.StrangerInfo import io.kritor.contact.StrangerInfoRequest import io.kritor.contact.VoteUserRequest import io.kritor.contact.VoteUserResponse -import io.kritor.contact.profileCard -import io.kritor.contact.strangerInfo -import io.kritor.contact.voteUserResponse import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import qq.service.QQInterfaces import qq.service.contact.ContactHelper import kotlin.coroutines.resume -import kotlin.coroutines.suspendCoroutine -internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() { +internal object ContactService : ContactServiceGrpcKt.ContactServiceCoroutineImplBase() { @Grpc("ContactService", "VoteUser") override suspend fun voteUser(request: VoteUserRequest): VoteUserResponse { - ContactHelper.voteUser(when(request.accountCase!!) { - VoteUserRequest.AccountCase.ACCOUNT_UIN -> request.accountUin - VoteUserRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong() - VoteUserRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("account not set") - ) - }, request.voteCount).onFailure { - throw StatusRuntimeException(Status.INTERNAL - .withDescription(it.stackTraceToString()) + ContactHelper.voteUser( + when (request.accountCase!!) { + VoteUserRequest.AccountCase.ACCOUNT_UIN -> request.accountUin + VoteUserRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong() + VoteUserRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("account not set") + ) + }, request.voteCount + ).onFailure { + throw StatusRuntimeException( + Status.INTERNAL + .withDescription(it.stackTraceToString()) ) } - return voteUserResponse { } + return VoteUserResponse.newBuilder().build() } @Grpc("ContactService", "GetProfileCard") @@ -55,21 +55,23 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl val uin = when (request.accountCase!!) { ProfileCardRequest.AccountCase.ACCOUNT_UIN -> request.accountUin ProfileCardRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong() - ProfileCardRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("account not set") + ProfileCardRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("account not set") ) } val contact = ContactHelper.getProfileCard(uin) contact.onFailure { - throw StatusRuntimeException(Status.INTERNAL - .withDescription(it.stackTraceToString()) + throw StatusRuntimeException( + Status.INTERNAL + .withDescription(it.stackTraceToString()) ) } contact.onSuccess { - return profileCard { + return ProfileCard.newBuilder().apply { this.uin = it.uin.toLong() this.uid = if (request.hasAccountUid()) request.accountUid else ContactHelper.getUidByUinAsync(it.uin.toLong()) @@ -81,11 +83,12 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl this.voteCnt = it.lVoteCount.toInt() this.qid = it.qid ?: "" this.isSchoolVerified = it.schoolVerifiedFlag - } + }.build() } - throw StatusRuntimeException(Status.INTERNAL - .withDescription("logic failed") + throw StatusRuntimeException( + Status.INTERNAL + .withDescription("logic failed") ) } @@ -93,13 +96,14 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl override suspend fun getStrangerInfo(request: StrangerInfoRequest): StrangerInfo { val userId = request.uin val info = ContactHelper.refreshAndGetProfileCard(userId).onFailure { - throw StatusRuntimeException(Status.INTERNAL - .withCause(it) - .withDescription("Unable to fetch stranger info") + throw StatusRuntimeException( + Status.INTERNAL + .withCause(it) + .withDescription("Unable to fetch stranger info") ) }.getOrThrow() - return strangerInfo { + return StrangerInfo.newBuilder().apply { this.uid = ContactHelper.getUidByUinAsync(userId) this.uin = (info.uin ?: "0").toLong() this.name = info.strNick ?: "" @@ -108,14 +112,14 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl this.voteCnt = info.lVoteCount.toInt() this.qid = info.qid ?: "" this.isSchoolVerified = info.schoolVerifiedFlag - this.ext = StrangerExt.newBuilder() - .setBigVip(info.bBigClubVipOpen == 1.toByte()) - .setHollywoodVip(info.bHollywoodVipOpen == 1.toByte()) - .setQqVip(info.bQQVipOpen == 1.toByte()) - .setSuperVip(info.bSuperQQOpen == 1.toByte()) - .setVoted(info.bVoted == 1.toByte()) - .build().toByteString() - } + this.ext = StrangerExt.newBuilder().apply { + this.bigVip = info.bBigClubVipOpen == 1.toByte() + this.hollywoodVip = info.bHollywoodVipOpen == 1.toByte() + this.qqVip = info.bQQVipOpen == 1.toByte() + this.superVip = info.bSuperQQOpen == 1.toByte() + this.voted = info.bVoted == 1.toByte() + }.build().toByteString() + }.build() } @Grpc("ContactService", "GetUid") diff --git a/xposed/src/main/java/kritor/service/DeveloperService.kt b/xposed/src/main/java/kritor/service/DeveloperService.kt index 4577fc6d..bf4b14d7 100644 --- a/xposed/src/main/java/kritor/service/DeveloperService.kt +++ b/xposed/src/main/java/kritor/service/DeveloperService.kt @@ -3,40 +3,31 @@ package kritor.service import com.google.protobuf.ByteString import com.tencent.mobileqq.fe.FEKit import com.tencent.mobileqq.qsec.qsecdandelionsdk.Dandelion -import io.kritor.developer.DeveloperServiceGrpcKt -import io.kritor.developer.EnergyRequest -import io.kritor.developer.EnergyResponse -import io.kritor.developer.SendPacketRequest -import io.kritor.developer.SendPacketResponse -import io.kritor.developer.SignRequest -import io.kritor.developer.SignResponse -import io.kritor.developer.energyResponse -import io.kritor.developer.sendPacketResponse -import io.kritor.developer.signResponse +import io.kritor.developer.* import qq.service.QQInterfaces internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCoroutineImplBase() { @Grpc("DeveloperService", "Sign") override suspend fun sign(request: SignRequest): SignResponse { - return signResponse { + return SignResponse.newBuilder().apply { val result = FEKit.getInstance().getSign(request.command, request.buffer.toByteArray(), request.seq, request.uin) this.sign = ByteString.copyFrom(result.sign) this.token = ByteString.copyFrom(result.token) this.extra = ByteString.copyFrom(result.extra) - } + }.build() } @Grpc("DeveloperService", "Energy") override suspend fun energy(request: EnergyRequest): EnergyResponse { - return energyResponse { + return EnergyResponse.newBuilder().apply { this.result = ByteString.copyFrom(Dandelion.getInstance().fly(request.data, request.salt.toByteArray())) - } + }.build() } @Grpc("DeveloperService", "SendPacket") override suspend fun sendPacket(request: SendPacketRequest): SendPacketResponse { - return sendPacketResponse { + return SendPacketResponse.newBuilder().apply { val fromServiceMsg = QQInterfaces.sendBufferAW(request.command, request.isProtobuf, request.requestBuffer.toByteArray()) if (fromServiceMsg?.wupBuffer == null) { this.isSuccess = false @@ -44,7 +35,7 @@ internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCorouti this.isSuccess = true this.responseBuffer = ByteString.copyFrom(fromServiceMsg.wupBuffer) } - } + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/EventService.kt b/xposed/src/main/java/kritor/service/EventService.kt index bededa2d..f04b7764 100644 --- a/xposed/src/main/java/kritor/service/EventService.kt +++ b/xposed/src/main/java/kritor/service/EventService.kt @@ -2,39 +2,40 @@ package kritor.service import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.event.EventRequest import io.kritor.event.EventServiceGrpcKt import io.kritor.event.EventStructure import io.kritor.event.EventType import io.kritor.event.RequestPushEvent -import io.kritor.event.eventStructure import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.channelFlow import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter -internal object EventService: EventServiceGrpcKt.EventServiceCoroutineImplBase() { +internal object EventService : EventServiceGrpcKt.EventServiceCoroutineImplBase() { override fun registerActiveListener(request: RequestPushEvent): Flow { return channelFlow { - when(request.type!!) { + when (request.type!!) { EventType.EVENT_TYPE_CORE_EVENT -> {} EventType.EVENT_TYPE_MESSAGE -> GlobalEventTransmitter.onMessageEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_MESSAGE this.message = it.second - }) + }.build()) } + EventType.EVENT_TYPE_NOTICE -> GlobalEventTransmitter.onRequestEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_NOTICE this.request = it - }) + }.build()) } + EventType.EVENT_TYPE_REQUEST -> GlobalEventTransmitter.onNoticeEvent { - send(eventStructure { + send(EventStructure.newBuilder().apply { this.type = EventType.EVENT_TYPE_NOTICE this.notice = it - }) + }.build()) } + EventType.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT) } } diff --git a/xposed/src/main/java/kritor/service/ForwardMessageService.kt b/xposed/src/main/java/kritor/service/ForwardMessageService.kt index a6b91f64..be6439cb 100644 --- a/xposed/src/main/java/kritor/service/ForwardMessageService.kt +++ b/xposed/src/main/java/kritor/service/ForwardMessageService.kt @@ -1,50 +1,85 @@ package kritor.service import com.tencent.qqnt.kernel.nativeinterface.MsgConstant -import com.tencent.qqnt.kernel.nativeinterface.MsgElement import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.message.Element -import io.kritor.message.ElementType -import io.kritor.message.ForwardMessageRequest -import io.kritor.message.ForwardMessageResponse -import io.kritor.message.ForwardMessageServiceGrpcKt -import io.kritor.message.Scene -import io.kritor.message.element -import io.kritor.message.forwardMessageResponse +import io.kritor.event.MessageEvent +import io.kritor.message.* import qq.service.contact.longPeer import qq.service.msg.ForwardMessageHelper import qq.service.msg.MessageHelper -import qq.service.msg.NtMsgConvertor +import qq.service.msg.toKritorResponseMessages -internal object ForwardMessageService: ForwardMessageServiceGrpcKt.ForwardMessageServiceCoroutineImplBase() { - @Grpc("ForwardMessageService", "ForwardMessage") - override suspend fun forwardMessage(request: ForwardMessageRequest): ForwardMessageResponse { +internal object ForwardMessageService : ForwardMessageServiceGrpcKt.ForwardMessageServiceCoroutineImplBase() { + @Grpc("ForwardMessageService", "UploadForwardMessage") + override suspend fun uploadForwardMessage(request: UploadForwardMessageRequest): UploadForwardMessageResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } - val forwardMessage = ForwardMessageHelper.uploadMultiMsg(contact.chatType, contact.longPeer().toString(), contact.guildId, request.messagesList).onFailure { + val forwardMessage = ForwardMessageHelper.uploadMultiMsg( + contact.chatType, + contact.longPeer().toString(), + contact.guildId, + request.messagesList + ).onFailure { throw StatusRuntimeException(Status.INTERNAL.withCause(it)) }.getOrThrow() - val uniseq = MessageHelper.generateMsgId(contact.chatType) - return forwardMessageResponse { - this.messageId = MessageHelper.sendMessage(contact, NtMsgConvertor.convertToNtMsgs(contact, uniseq, arrayListOf(element { - this.type = ElementType.FORWARD - this.forward = forwardMessage - })), request.retryCount, uniseq).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withCause(it)) - }.getOrThrow() + return UploadForwardMessageResponse.newBuilder().apply { this.resId = forwardMessage.id - } + }.build() + } + + @Grpc("ForwardMessageService", "DownloadForwardMessage") + override suspend fun downloadForwardMessage(request: DownloadForwardMessageRequest): DownloadForwardMessageResponse { + return DownloadForwardMessageResponse.newBuilder().apply { + this.addAllMessages( + MessageHelper.getForwardMsg(request.resId).onFailure { + throw StatusRuntimeException(Status.INTERNAL.withCause(it)) + }.getOrThrow().map { detail -> + MessageEvent.newBuilder().apply { + this.time = detail.time + this.messageId = detail.qqMsgId + this.messageSeq = detail.msgSeq + this.contact = Contact.newBuilder().apply { + this.scene = when (detail.msgType) { + MsgConstant.KCHATTYPEC2C -> Scene.FRIEND + MsgConstant.KCHATTYPEGROUP -> Scene.GROUP + MsgConstant.KCHATTYPEGUILD -> Scene.GUILD + MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Scene.STRANGER_FROM_GROUP + MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN -> Scene.NEARBY + else -> Scene.STRANGER + } + this.peer = detail.peerId.toString() + }.build() + this.sender = Sender.newBuilder().apply { + this.uin = detail.sender.userId + this.nick = detail.sender.nickName + this.uid = detail.sender.uid + }.build() + detail.message?.elements?.toKritorResponseMessages( + com.tencent.qqnt.kernel.nativeinterface.Contact( + detail.msgType, + detail.peerId.toString(), + null + ) + )?.let { + this.addAllElements(it) + } + }.build() + } + ) + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/FriendService.kt b/xposed/src/main/java/kritor/service/FriendService.kt index bd45e4b8..f2863e86 100644 --- a/xposed/src/main/java/kritor/service/FriendService.kt +++ b/xposed/src/main/java/kritor/service/FriendService.kt @@ -2,12 +2,7 @@ package kritor.service import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.friend.FriendServiceGrpcKt -import io.kritor.friend.GetFriendListRequest -import io.kritor.friend.GetFriendListResponse -import io.kritor.friend.friendData -import io.kritor.friend.friendExt -import io.kritor.friend.getFriendListResponse +import io.kritor.friend.* import qq.service.contact.ContactHelper import qq.service.friend.FriendHelper @@ -20,9 +15,9 @@ internal object FriendService: FriendServiceGrpcKt.FriendServiceCoroutineImplBas ) }.getOrThrow() - return getFriendListResponse { + return GetFriendListResponse.newBuilder().apply { friendList.forEach { - this.friendList.add(friendData { + this.addFriendList(FriendData.newBuilder().apply { uin = it.uin.toLong() uid = ContactHelper.getUidByUinAsync(uin) qid = "" @@ -32,10 +27,10 @@ internal object FriendService: FriendServiceGrpcKt.FriendServiceCoroutineImplBas level = 0 gender = it.gender.toInt() groupId = it.groupid - ext = friendExt {}.toByteString() + ext = FriendExt.newBuilder().build().toByteString() }) } - } + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/GroupFileService.kt b/xposed/src/main/java/kritor/service/GroupFileService.kt index b67685a3..345b248f 100644 --- a/xposed/src/main/java/kritor/service/GroupFileService.kt +++ b/xposed/src/main/java/kritor/service/GroupFileService.kt @@ -15,7 +15,6 @@ import qq.service.QQInterfaces import qq.service.file.GroupFileHelper import qq.service.file.GroupFileHelper.getGroupFileSystemInfo import tencent.im.oidb.cmd0x6d6.oidb_0x6d6 -import tencent.im.oidb.cmd0x6d8.oidb_0x6d8 import tencent.im.oidb.oidb_sso internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCoroutineImplBase() { @@ -42,10 +41,10 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti if (rsp.createFolder?.retCode != 0) { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to create folder: ${rsp.createFolder?.retCode}")) } - return createFolderResponse { + return CreateFolderResponse.newBuilder().apply { this.id = rsp.createFolder?.folderInfo?.folderId ?: "" this.usedSpace = 0 - } + }.build() } @Grpc("GroupFileService", "DeleteFolder") @@ -66,7 +65,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti if (rsp.deleteFolder?.retCode != 0) { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to delete folder: ${rsp.deleteFolder?.retCode}")) } - return deleteFolderResponse { } + return DeleteFolderResponse.newBuilder().build() } @Grpc("GroupFileService", "DeleteFile") @@ -93,7 +92,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti if (rsp.delete_file_rsp.int32_ret_code.get() != 0) { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to delete file: ${rsp.delete_file_rsp.int32_ret_code.get()}")) } - return deleteFileResponse { } + return DeleteFileResponse.newBuilder().build() } @Grpc("GroupFileService", "RenameFolder") @@ -115,7 +114,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti if (rsp.renameFolder?.retCode != 0) { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to rename folder: ${rsp.renameFolder?.retCode}")) } - return renameFolderResponse { } + return RenameFolderResponse.newBuilder().build() } @Grpc("GroupFileService", "GetFileSystemInfo") @@ -125,11 +124,11 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti @Grpc("GroupFileService", "GetRootFiles") override suspend fun getRootFiles(request: GetRootFilesRequest): GetRootFilesResponse { - return getRootFilesResponse { + return GetRootFilesResponse.newBuilder().apply { val response = GroupFileHelper.getGroupFiles(request.groupId) - this.files.addAll(response.filesList) - this.folders.addAll(response.foldersList) - } + this.addAllFiles(response.filesList) + this.addAllFolders(response.foldersList) + }.build() } @Grpc("GroupFileService", "GetFiles") diff --git a/xposed/src/main/java/kritor/service/GroupService.kt b/xposed/src/main/java/kritor/service/GroupService.kt index 8f986273..ac3ee10f 100644 --- a/xposed/src/main/java/kritor/service/GroupService.kt +++ b/xposed/src/main/java/kritor/service/GroupService.kt @@ -2,212 +2,186 @@ package kritor.service import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.group.BanMemberRequest -import io.kritor.group.BanMemberResponse -import io.kritor.group.GetGroupHonorRequest -import io.kritor.group.GetGroupHonorResponse -import io.kritor.group.GetGroupInfoRequest -import io.kritor.group.GetGroupInfoResponse -import io.kritor.group.GetGroupListRequest -import io.kritor.group.GetGroupListResponse -import io.kritor.group.GetGroupMemberInfoRequest -import io.kritor.group.GetGroupMemberInfoResponse -import io.kritor.group.GetGroupMemberListRequest -import io.kritor.group.GetGroupMemberListResponse -import io.kritor.group.GetNotJoinedGroupInfoRequest -import io.kritor.group.GetNotJoinedGroupInfoResponse -import io.kritor.group.GetProhibitedUserListRequest -import io.kritor.group.GetProhibitedUserListResponse -import io.kritor.group.GetRemainCountAtAllRequest -import io.kritor.group.GetRemainCountAtAllResponse -import io.kritor.group.GroupServiceGrpcKt -import io.kritor.group.KickMemberRequest -import io.kritor.group.KickMemberResponse -import io.kritor.group.LeaveGroupRequest -import io.kritor.group.LeaveGroupResponse -import io.kritor.group.ModifyGroupNameRequest -import io.kritor.group.ModifyGroupNameResponse -import io.kritor.group.ModifyGroupRemarkRequest -import io.kritor.group.ModifyGroupRemarkResponse -import io.kritor.group.ModifyMemberCardRequest -import io.kritor.group.ModifyMemberCardResponse -import io.kritor.group.PokeMemberRequest -import io.kritor.group.PokeMemberResponse -import io.kritor.group.SetGroupAdminRequest -import io.kritor.group.SetGroupAdminResponse -import io.kritor.group.SetGroupUniqueTitleRequest -import io.kritor.group.SetGroupUniqueTitleResponse -import io.kritor.group.SetGroupWholeBanRequest -import io.kritor.group.SetGroupWholeBanResponse -import io.kritor.group.banMemberResponse -import io.kritor.group.getGroupHonorResponse -import io.kritor.group.getGroupInfoResponse -import io.kritor.group.getGroupListResponse -import io.kritor.group.getGroupMemberInfoResponse -import io.kritor.group.getGroupMemberListResponse -import io.kritor.group.getNotJoinedGroupInfoResponse -import io.kritor.group.getProhibitedUserListResponse -import io.kritor.group.getRemainCountAtAllResponse -import io.kritor.group.groupHonorInfo -import io.kritor.group.groupMemberInfo -import io.kritor.group.kickMemberResponse -import io.kritor.group.leaveGroupResponse -import io.kritor.group.modifyGroupNameResponse -import io.kritor.group.modifyGroupRemarkResponse -import io.kritor.group.modifyMemberCardResponse -import io.kritor.group.notJoinedGroupInfo -import io.kritor.group.pokeMemberResponse -import io.kritor.group.prohibitedUserInfo -import io.kritor.group.setGroupAdminResponse -import io.kritor.group.setGroupUniqueTitleResponse -import io.kritor.group.setGroupWholeBanResponse +import io.kritor.group.* import moe.fuqiuluo.shamrock.helper.TroopHonorHelper.decodeHonor import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty import qq.service.contact.ContactHelper import qq.service.group.GroupHelper -internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() { +internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase() { @Grpc("GroupService", "BanMember") override suspend fun banMember(request: BanMemberRequest): BanMemberResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } - GroupHelper.banMember(request.groupId, when(request.targetCase!!) { - BanMemberRequest.TargetCase.TARGET_UIN -> request.targetUin - BanMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }, request.duration) + GroupHelper.banMember( + request.groupId, when (request.targetCase!!) { + BanMemberRequest.TargetCase.TARGET_UIN -> request.targetUin + BanMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + }, request.duration + ) - return banMemberResponse { + return BanMemberResponse.newBuilder().apply { groupId = request.groupId - } + }.build() } - @Grpc("GroupService", "PokeMember", ) + @Grpc("GroupService", "PokeMember") override suspend fun pokeMember(request: PokeMemberRequest): PokeMemberResponse { - GroupHelper.pokeMember(request.groupId, when(request.targetCase!!) { - PokeMemberRequest.TargetCase.TARGET_UIN -> request.targetUin - PokeMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }) - return pokeMemberResponse { } + GroupHelper.pokeMember( + request.groupId, when (request.targetCase!!) { + PokeMemberRequest.TargetCase.TARGET_UIN -> request.targetUin + PokeMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + } + ) + return PokeMemberResponse.newBuilder().build() } @Grpc("GroupService", "KickMember") override suspend fun kickMember(request: KickMemberRequest): KickMemberResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } - GroupHelper.kickMember(request.groupId, request.rejectAddRequest, if (request.hasKickReason()) request.kickReason else "", when(request.targetCase!!) { - KickMemberRequest.TargetCase.TARGET_UIN -> request.targetUin - KickMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }) - return kickMemberResponse { } + GroupHelper.kickMember( + request.groupId, + request.rejectAddRequest, + if (request.hasKickReason()) request.kickReason else "", + when (request.targetCase!!) { + KickMemberRequest.TargetCase.TARGET_UIN -> request.targetUin + KickMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + } + ) + return KickMemberResponse.newBuilder().build() } @Grpc("GroupService", "LeaveGroup") override suspend fun leaveGroup(request: LeaveGroupRequest): LeaveGroupResponse { GroupHelper.resignTroop(request.groupId.toString()) - return leaveGroupResponse { } + return LeaveGroupResponse.newBuilder().build() } @Grpc("GroupService", "ModifyMemberCard") override suspend fun modifyMemberCard(request: ModifyMemberCardRequest): ModifyMemberCardResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } - GroupHelper.modifyGroupMemberCard(request.groupId, when(request.targetCase!!) { - ModifyMemberCardRequest.TargetCase.TARGET_UIN -> request.targetUin - ModifyMemberCardRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }, request.card) - return modifyMemberCardResponse { } + GroupHelper.modifyGroupMemberCard( + request.groupId, when (request.targetCase!!) { + ModifyMemberCardRequest.TargetCase.TARGET_UIN -> request.targetUin + ModifyMemberCardRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid) + .toLong() + + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + }, request.card + ) + return ModifyMemberCardResponse.newBuilder().build() } @Grpc("GroupService", "ModifyGroupName") override suspend fun modifyGroupName(request: ModifyGroupNameRequest): ModifyGroupNameResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } GroupHelper.modifyTroopName(request.groupId.toString(), request.groupName) - return modifyGroupNameResponse { } + return ModifyGroupNameResponse.newBuilder().build() } @Grpc("GroupService", "ModifyGroupRemark") override suspend fun modifyGroupRemark(request: ModifyGroupRemarkRequest): ModifyGroupRemarkResponse { GroupHelper.modifyGroupRemark(request.groupId, request.remark) - return modifyGroupRemarkResponse { } + return ModifyGroupRemarkResponse.newBuilder().build() } @Grpc("GroupService", "SetGroupAdmin") override suspend fun setGroupAdmin(request: SetGroupAdminRequest): SetGroupAdminResponse { if (!GroupHelper.isOwner(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } - GroupHelper.setGroupAdmin(request.groupId, when(request.targetCase!!) { - SetGroupAdminRequest.TargetCase.TARGET_UIN -> request.targetUin - SetGroupAdminRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }, request.isAdmin) + GroupHelper.setGroupAdmin( + request.groupId, when (request.targetCase!!) { + SetGroupAdminRequest.TargetCase.TARGET_UIN -> request.targetUin + SetGroupAdminRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + }, request.isAdmin + ) - return setGroupAdminResponse { } + return SetGroupAdminResponse.newBuilder().build() } @Grpc("GroupService", "SetGroupUniqueTitle") override suspend fun setGroupUniqueTitle(request: SetGroupUniqueTitleRequest): SetGroupUniqueTitleResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } - GroupHelper.setGroupUniqueTitle(request.groupId.toString(), when(request.targetCase!!) { - SetGroupUniqueTitleRequest.TargetCase.TARGET_UIN -> request.targetUin - SetGroupUniqueTitleRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") - ) - }.toString(), request.uniqueTitle) + GroupHelper.setGroupUniqueTitle( + request.groupId.toString(), when (request.targetCase!!) { + SetGroupUniqueTitleRequest.TargetCase.TARGET_UIN -> request.targetUin + SetGroupUniqueTitleRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid) + .toLong() - return setGroupUniqueTitleResponse { } + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + }.toString(), request.uniqueTitle + ) + + return SetGroupUniqueTitleResponse.newBuilder().build() } @Grpc("GroupService", "SetGroupWholeBan") override suspend fun setGroupWholeBan(request: SetGroupWholeBanRequest): SetGroupWholeBanResponse { if (!GroupHelper.isAdmin(request.groupId.toString())) { - throw StatusRuntimeException(Status.PERMISSION_DENIED - .withDescription("You are not admin of this group") + throw StatusRuntimeException( + Status.PERMISSION_DENIED + .withDescription("You are not admin of this group") ) } GroupHelper.setGroupWholeBan(request.groupId, request.isBan) - return setGroupWholeBanResponse { } + return SetGroupWholeBanResponse.newBuilder().build() } @Grpc("GroupService", "GetGroupInfo") @@ -215,18 +189,20 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() val groupInfo = GroupHelper.getGroupInfo(request.groupId.toString(), true).onFailure { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group info").withCause(it)) }.getOrThrow() - return getGroupInfoResponse { - this.groupInfo = io.kritor.group.groupInfo { + return GetGroupInfoResponse.newBuilder().apply { + this.groupInfo = GroupInfo.newBuilder().apply { groupId = groupInfo.troopcode.toLong() - groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName } ?: "" + groupName = + groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName } + ?: "" groupRemark = groupInfo.troopRemark ?: "" owner = groupInfo.troopowneruin?.toLong() ?: 0 - admins.addAll(GroupHelper.getAdminList(groupId)) + addAllAdmins(GroupHelper.getAdminList(groupId)) maxMemberCount = groupInfo.wMemberMax memberCount = groupInfo.wMemberNum groupUin = groupInfo.troopuin?.toLong() ?: 0 - } - } + }.build() + }.build() } @Grpc("GroupService", "GetGroupList") @@ -234,36 +210,45 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() val groupList = GroupHelper.getGroupList(if (request.hasRefresh()) request.refresh else false).onFailure { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group list").withCause(it)) }.getOrThrow() - return getGroupListResponse { + return GetGroupListResponse.newBuilder().apply { groupList.forEach { groupInfo -> - this.groupInfo.add(io.kritor.group.groupInfo { + this.addGroupInfo(GroupInfo.newBuilder().apply { groupId = groupInfo.troopcode.toLong() - groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName } ?: "" + groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark } + .ifNullOrEmpty { groupInfo.newTroopName } ?: "" groupRemark = groupInfo.troopRemark ?: "" owner = groupInfo.troopowneruin?.toLong() ?: 0 - admins.addAll(GroupHelper.getAdminList(groupId)) + addAllAdmins(GroupHelper.getAdminList(groupId)) maxMemberCount = groupInfo.wMemberMax memberCount = groupInfo.wMemberNum groupUin = groupInfo.troopuin?.toLong() ?: 0 }) } - } + }.build() } @Grpc("GroupService", "GetGroupMemberInfo") override suspend fun getGroupMemberInfo(request: GetGroupMemberInfoRequest): GetGroupMemberInfoResponse { - val memberInfo = GroupHelper.getTroopMemberInfoByUin(request.groupId.toString(), when(request.targetCase!!) { - GetGroupMemberInfoRequest.TargetCase.UIN -> request.uin - GetGroupMemberInfoRequest.TargetCase.UID -> ContactHelper.getUinByUidAsync(request.uid).toLong() - else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT - .withDescription("target not set") + val memberInfo = GroupHelper.getTroopMemberInfoByUin( + request.groupId.toString(), when (request.targetCase!!) { + GetGroupMemberInfoRequest.TargetCase.UIN -> request.uin + GetGroupMemberInfoRequest.TargetCase.UID -> ContactHelper.getUinByUidAsync(request.uid).toLong() + else -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT + .withDescription("target not set") + ) + }.toString() + ).onFailure { + throw StatusRuntimeException( + Status.INTERNAL.withDescription("unable to get group member info").withCause(it) ) - }.toString()).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member info").withCause(it)) }.getOrThrow() - return getGroupMemberInfoResponse { - groupMemberInfo = groupMemberInfo { - uid = if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.UID) request.uid else ContactHelper.getUidByUinAsync(request.uin) + return GetGroupMemberInfoResponse.newBuilder().apply { + groupMemberInfo = GroupMemberInfo.newBuilder().apply { + uid = + if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.UID) request.uid else ContactHelper.getUidByUinAsync( + request.uin + ) uin = memberInfo.memberuin?.toLong() ?: 0 nick = memberInfo.troopnick .ifNullOrEmpty { memberInfo.hwName } @@ -279,24 +264,29 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() shutUpTimestamp = memberInfo.gagTimeStamp distance = memberInfo.distance - honor.addAll((memberInfo.honorList ?: "") + addAllHonor((memberInfo.honorList ?: "") .split("|") .filter { it.isNotBlank() } .map { it.toInt() }) unfriendly = false cardChangeable = GroupHelper.isAdmin(request.groupId.toString()) - } - } + }.build() + }.build() } @Grpc("GroupService", "GetGroupMemberList") override suspend fun getGroupMemberList(request: GetGroupMemberListRequest): GetGroupMemberListResponse { - val memberList = GroupHelper.getGroupMemberList(request.groupId.toString(), if (request.hasRefresh()) request.refresh else false).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member list").withCause(it)) + val memberList = GroupHelper.getGroupMemberList( + request.groupId.toString(), + if (request.hasRefresh()) request.refresh else false + ).onFailure { + throw StatusRuntimeException( + Status.INTERNAL.withDescription("unable to get group member list").withCause(it) + ) }.getOrThrow() - return getGroupMemberListResponse { + return GetGroupMemberListResponse.newBuilder().apply { memberList.forEach { memberInfo -> - this.groupMemberInfo.add(groupMemberInfo { + this.addGroupMemberInfo(GroupMemberInfo.newBuilder().apply { uid = ContactHelper.getUidByUinAsync(memberInfo.memberuin?.toLong() ?: 0) uin = memberInfo.memberuin?.toLong() ?: 0 nick = memberInfo.troopnick @@ -313,7 +303,7 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() shutUpTimestamp = memberInfo.gagTimeStamp distance = memberInfo.distance - honor.addAll((memberInfo.honorList ?: "") + addAllHonor((memberInfo.honorList ?: "") .split("|") .filter { it.isNotBlank() } .map { it.toInt() }) @@ -321,23 +311,25 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() cardChangeable = GroupHelper.isAdmin(request.groupId.toString()) }) } - } + }.build() } @Grpc("GroupService", "GetProhibitedUserList") override suspend fun getProhibitedUserList(request: GetProhibitedUserListRequest): GetProhibitedUserListResponse { val prohibitedList = GroupHelper.getProhibitedMemberList(request.groupId).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get prohibited user list").withCause(it)) + throw StatusRuntimeException( + Status.INTERNAL.withDescription("unable to get prohibited user list").withCause(it) + ) }.getOrThrow() - return getProhibitedUserListResponse { + return GetProhibitedUserListResponse.newBuilder().apply { prohibitedList.forEach { - this.prohibitedUserInfo.add(prohibitedUserInfo { + this.addProhibitedUserInfo(ProhibitedUserInfo.newBuilder().apply { uid = ContactHelper.getUidByUinAsync(it.memberUin) uin = it.memberUin prohibitedTime = it.shutuptimestap }) } - } + }.build() } @Grpc("GroupService", "GetRemainCountAtAll") @@ -345,20 +337,22 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() val remainAtAllRsp = GroupHelper.getGroupRemainAtAllRemain(request.groupId).onFailure { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get remain count").withCause(it)) }.getOrThrow() - return getRemainCountAtAllResponse { + return GetRemainCountAtAllResponse.newBuilder().apply { accessAtAll = remainAtAllRsp.bool_can_at_all.get() remainCountForGroup = remainAtAllRsp.uint32_remain_at_all_count_for_group.get() remainCountForSelf = remainAtAllRsp.uint32_remain_at_all_count_for_uin.get() - } + }.build() } @Grpc("GroupService", "GetNotJoinedGroupInfo") override suspend fun getNotJoinedGroupInfo(request: GetNotJoinedGroupInfoRequest): GetNotJoinedGroupInfoResponse { val groupInfo = GroupHelper.getNotJoinedGroupInfo(request.groupId).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get not joined group info").withCause(it)) + throw StatusRuntimeException( + Status.INTERNAL.withDescription("unable to get not joined group info").withCause(it) + ) }.getOrThrow() - return getNotJoinedGroupInfoResponse { - this.groupInfo = notJoinedGroupInfo { + return GetNotJoinedGroupInfoResponse.newBuilder().apply { + this.groupInfo = NotJoinedGroupInfo.newBuilder().apply { groupId = groupInfo.groupId groupName = groupInfo.groupName owner = groupInfo.owner @@ -368,15 +362,17 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() createTime = groupInfo.createTime.toInt() groupFlag = groupInfo.groupFlag groupFlagExt = groupInfo.groupFlagExt - } - } + }.build() + }.build() } @Grpc("GroupService", "GetGroupHonor") override suspend fun getGroupHonor(request: GetGroupHonorRequest): GetGroupHonorResponse { - return getGroupHonorResponse { + return GetGroupHonorResponse.newBuilder().apply { GroupHelper.getGroupMemberList(request.groupId.toString(), true).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member list").withCause(it)) + throw StatusRuntimeException( + Status.INTERNAL.withDescription("unable to get group member list").withCause(it) + ) }.onSuccess { memberList -> memberList.forEach { member -> (member.honorList ?: "").split("|") @@ -384,7 +380,7 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() .map { it.toInt() }.forEach { val honor = decodeHonor(member.memberuin.toLong(), it, member.mHonorRichFlag) if (honor != null) { - groupHonorInfo.add(groupHonorInfo { + addGroupHonorInfo(GroupHonorInfo.newBuilder().apply { uid = ContactHelper.getUidByUinAsync(member.memberuin.toLong()) uin = member.memberuin.toLong() nick = member.troopnick @@ -400,6 +396,6 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() } } } - } + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/KritorService.kt b/xposed/src/main/java/kritor/service/KritorService.kt index 185bc6a7..28a44c0f 100644 --- a/xposed/src/main/java/kritor/service/KritorService.kt +++ b/xposed/src/main/java/kritor/service/KritorService.kt @@ -4,25 +4,7 @@ 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.GetDeviceBatteryRequest -import io.kritor.core.GetDeviceBatteryResponse -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.getDeviceBatteryResponse -import io.kritor.core.getVersionResponse -import io.kritor.core.switchAccountResponse +import io.kritor.core.* import moe.fuqiuluo.shamrock.tools.ShamrockVersion import moe.fuqiuluo.shamrock.utils.DownloadUtils import moe.fuqiuluo.shamrock.utils.FileUtils @@ -30,18 +12,17 @@ import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MMKVFetcher import moe.fuqiuluo.shamrock.utils.PlatformUtils import mqq.app.MobileQQ -import qq.service.QQInterfaces import qq.service.QQInterfaces.Companion.app import qq.service.contact.ContactHelper import java.io.File -internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBase() { +internal object KritorService : KritorServiceGrpcKt.KritorServiceCoroutineImplBase() { @Grpc("KritorService", "GetVersion") override suspend fun getVersion(request: GetVersionRequest): GetVersionResponse { - return getVersionResponse { + return GetVersionResponse.newBuilder().apply { this.version = ShamrockVersion this.appName = "Shamrock" - } + }.build() } @Grpc("KritorService", "ClearCache") @@ -49,16 +30,16 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas FileUtils.clearCache() MMKVFetcher.mmkvWithId("audio2silk") .clear() - return clearCacheResponse {} + return ClearCacheResponse.newBuilder().build() } @Grpc("KritorService", "GetCurrentAccount") override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse { - return getCurrentAccountResponse { + return GetCurrentAccountResponse.newBuilder().apply { this.accountName = if (app is QQAppInterface) app.currentNickname else "unknown" this.accountUid = app.currentUid ?: "" this.accountUin = (app.currentUin ?: "0").toLong() - } + }.build() } @Grpc("KritorService", "DownloadFile") @@ -80,13 +61,14 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas if (request.hasBase64()) { val bytes = Base64.decode(request.base64, Base64.DEFAULT) tmp.writeBytes(bytes) - } else if(request.hasUrl()) { - if(!DownloadUtils.download( + } 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")) } } @@ -100,18 +82,22 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas } } - return downloadFileResponse { + return DownloadFileResponse.newBuilder().apply { this.fileMd5 = MD5.genFileMd5Hex(tmp.absolutePath) this.fileAbsolutePath = tmp.absolutePath - } + }.build() } @Grpc("KritorService", "SwitchAccount") override suspend fun switchAccount(request: SwitchAccountRequest): SwitchAccountResponse { - val uin = when(request.accountCase!!) { + val uin = when (request.accountCase!!) { SwitchAccountRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid) SwitchAccountRequest.AccountCase.ACCOUNT_UIN -> request.accountUin.toString() - SwitchAccountRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("account not found")) + SwitchAccountRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException( + Status.INVALID_ARGUMENT.withDescription( + "account not found" + ) + ) } val account = MobileQQ.getMobileQQ().allAccounts.firstOrNull { it.uin == uin } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("account not found")) @@ -120,17 +106,17 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas }.onFailure { throw StatusRuntimeException(Status.INTERNAL.withCause(it).withDescription("failed to switch account")) } - return switchAccountResponse { } + return SwitchAccountResponse.newBuilder().build() } @Grpc("KritorService", "GetDeviceBattery") override suspend fun getDeviceBattery(request: GetDeviceBatteryRequest): GetDeviceBatteryResponse { - return getDeviceBatteryResponse { + return GetDeviceBatteryResponse.newBuilder().apply { PlatformUtils.getDeviceBattery().let { this.battery = it.battery this.scale = it.scale this.status = it.status } - } + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/MessageService.kt b/xposed/src/main/java/kritor/service/MessageService.kt index 0d2ad7b9..0ff498c4 100644 --- a/xposed/src/main/java/kritor/service/MessageService.kt +++ b/xposed/src/main/java/kritor/service/MessageService.kt @@ -7,48 +7,8 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.msg.api.IMsgService import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.message.ClearMessagesRequest -import io.kritor.message.ClearMessagesResponse -import io.kritor.message.DeleteEssenceMsgRequest -import io.kritor.message.DeleteEssenceMsgResponse -import io.kritor.message.GetEssenceMessagesRequest -import io.kritor.message.GetEssenceMessagesResponse -import io.kritor.message.GetForwardMessagesRequest -import io.kritor.message.GetForwardMessagesResponse -import io.kritor.message.GetHistoryMessageRequest -import io.kritor.message.GetHistoryMessageResponse -import io.kritor.message.GetMessageBySeqRequest -import io.kritor.message.GetMessageBySeqResponse -import io.kritor.message.GetMessageRequest -import io.kritor.message.GetMessageResponse -import io.kritor.message.MessageServiceGrpcKt -import io.kritor.message.RecallMessageRequest -import io.kritor.message.RecallMessageResponse -import io.kritor.message.Scene -import io.kritor.message.SendMessageByResIdRequest -import io.kritor.message.SendMessageByResIdResponse -import io.kritor.message.SendMessageRequest -import io.kritor.message.SendMessageResponse -import io.kritor.message.SetEssenceMessageRequest -import io.kritor.message.SetEssenceMessageResponse -import io.kritor.message.SetMessageCommentEmojiRequest -import io.kritor.message.SetMessageCommentEmojiResponse -import io.kritor.message.clearMessagesResponse -import io.kritor.message.contact -import io.kritor.message.deleteEssenceMsgResponse -import io.kritor.message.essenceMessage -import io.kritor.message.getEssenceMessagesResponse -import io.kritor.message.getForwardMessagesResponse -import io.kritor.message.getHistoryMessageResponse -import io.kritor.message.getMessageBySeqResponse -import io.kritor.message.getMessageResponse -import io.kritor.message.messageBody -import io.kritor.message.recallMessageResponse -import io.kritor.message.sendMessageByResIdResponse -import io.kritor.message.sendMessageResponse -import io.kritor.message.sender -import io.kritor.message.setEssenceMessageResponse -import io.kritor.message.setMessageCommentEmojiResponse +import io.kritor.event.MessageEvent +import io.kritor.message.* import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.shamrock.helper.Level @@ -75,27 +35,34 @@ import kotlin.coroutines.resume import kotlin.random.Random import kotlin.random.nextUInt -internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImplBase() { +internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImplBase() { @Grpc("MessageService", "SendMessage") override suspend fun sendMessage(request: SendMessageRequest): SendMessageResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val uniseq = MessageHelper.generateMsgId(contact.chatType) - return sendMessageResponse { - this.messageId = MessageHelper.sendMessage(contact, NtMsgConvertor.convertToNtMsgs(contact, uniseq, request.elementsList), request.retryCount, uniseq).onFailure { + return SendMessageResponse.newBuilder().apply { + this.messageId = MessageHelper.sendMessage( + contact, + NtMsgConvertor.convertToNtMsgs(contact, uniseq, request.elementsList), + request.retryCount, + uniseq + ).onFailure { throw StatusRuntimeException(Status.INTERNAL.withCause(it)) }.getOrThrow() - } + }.build() } @Grpc("MessageService", "SendMessageByResId") @@ -125,7 +92,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl msgVia = 0u ) QQInterfaces.sendBuffer("MessageSvc.PbSendMsg", true, req.toByteArray()) - return sendMessageByResIdResponse { } + return SendMessageByResIdResponse.newBuilder().build() } @Grpc("MessageService", "ClearMessages") @@ -134,7 +101,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl val kernelService = NTServiceFetcher.kernelService val sessionService = kernelService.wrapperSession val service = sessionService.msgService - val chatType = when(contact.scene!!) { + val chatType = when (contact.scene!!) { Scene.GROUP -> MsgConstant.KCHATTYPEGROUP Scene.FRIEND -> MsgConstant.KCHATTYPEC2C Scene.GUILD -> MsgConstant.KCHATTYPEGUILD @@ -144,21 +111,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) } service.clearMsgRecords(Contact(chatType, contact.peer, contact.subPeer), null) - return clearMessagesResponse { } + return ClearMessagesResponse.newBuilder().build() } @Grpc("MessageService", "RecallMessage") override suspend fun recallMessage(request: RecallMessageRequest): RecallMessageResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val kernelService = NTServiceFetcher.kernelService val sessionService = kernelService.wrapperSession @@ -169,62 +138,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } - return recallMessageResponse {} - } - - @Grpc("MessageService", "GetForwardMessages") - override suspend fun getForwardMessages(request: GetForwardMessagesRequest): GetForwardMessagesResponse { - return getForwardMessagesResponse { - MessageHelper.getForwardMsg(request.resId).onFailure { - throw StatusRuntimeException(Status.INTERNAL.withCause(it)) - }.getOrThrow().forEach { detail -> - messages.add(messageBody { - val peer = when (scene) { - Scene.GROUP -> detail.groupId.toString() - Scene.FRIEND -> detail.sender.userId.toString() - else -> detail.peerId.toString() - } - - this.time = detail.time - this.scene = when(detail.msgType) { - MsgConstant.KCHATTYPEC2C -> Scene.FRIEND - MsgConstant.KCHATTYPEGROUP -> Scene.GROUP - MsgConstant.KCHATTYPEGUILD -> Scene.GUILD - MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Scene.STRANGER_FROM_GROUP - MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN -> Scene.NEARBY - else -> Scene.STRANGER - } - this.messageId = detail.qqMsgId - this.messageSeq = detail.msgSeq - this.contact = contact { - this.scene = scene - this.peer = peer - } - this.sender = sender { - this.uin = detail.sender.userId - this.nick = detail.sender.nickName - this.uid = detail.sender.uid - } - detail.message?.elements?.toKritorResponseMessages(Contact(detail.msgType, peer, null))?.let { - this.elements.addAll(it) - } - }) - } - } + return RecallMessageResponse.newBuilder().build() } @Grpc("MessageService", "GetMessage") override suspend fun getMessage(request: GetMessageRequest): GetMessageResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val msg: MsgRecord = withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) @@ -242,34 +172,35 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) - return getMessageResponse { - this.message = messageBody { + return GetMessageResponse.newBuilder().apply { + this.message = MessageEvent.newBuilder().apply { this.messageId = msg.msgId - this.scene = request.contact.scene this.contact = request.contact - this.sender = sender { + this.sender = Sender.newBuilder().apply { this.uin = msg.senderUin this.nick = msg.sendNickName ?: "" this.uid = msg.senderUid ?: "" - } + }.build() this.messageSeq = msg.msgSeq - this.elements.addAll(msg.elements.toKritorReqMessages(contact)) - } - } + this.addAllElements(msg.elements.toKritorReqMessages(contact)) + }.build() + }.build() } @Grpc("MessageService", "GetMessageBySeq") override suspend fun getMessageBySeq(request: GetMessageBySeqRequest): GetMessageBySeqResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val msg: MsgRecord = withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) @@ -287,34 +218,35 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) - return getMessageBySeqResponse { - this.message = messageBody { + return GetMessageBySeqResponse.newBuilder().apply { + this.message = MessageEvent.newBuilder().apply { this.messageId = msg.msgId - this.scene = request.contact.scene this.contact = request.contact - this.sender = sender { + this.sender = Sender.newBuilder().apply { this.uin = msg.senderUin this.nick = msg.sendNickName ?: "" this.uid = msg.senderUid ?: "" - } + }.build() this.messageSeq = msg.msgSeq - this.elements.addAll(msg.elements.toKritorReqMessages(contact)) - } - } + this.addAllElements(msg.elements.toKritorReqMessages(contact)) + }.build() + }.build() } @Grpc("MessageService", "GetHistoryMessage") override suspend fun getHistoryMessage(request: GetHistoryMessageRequest): GetHistoryMessageResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val msgs: List = withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) @@ -332,22 +264,21 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Messages not found")) - return getHistoryMessageResponse { + return GetHistoryMessageResponse.newBuilder().apply { msgs.forEach { - messages.add(messageBody { + addMessages(MessageEvent.newBuilder().apply { this.messageId = it.msgId - this.scene = request.contact.scene this.contact = request.contact - this.sender = sender { + this.sender = Sender.newBuilder().apply { this.uin = it.senderUin this.nick = it.sendNickName ?: "" this.uid = it.senderUid ?: "" - } + }.build() this.messageSeq = it.msgSeq - this.elements.addAll(it.elements.toKritorReqMessages(contact)) + this.addAllElements(it.elements.toKritorReqMessages(contact)) }) } - } + }.build() } @Grpc("MessageService", "DeleteEssenceMsg") @@ -368,19 +299,19 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) - if(MessageHelper.deleteEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null) + if (MessageHelper.deleteEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null) throw StatusRuntimeException(Status.NOT_FOUND.withDescription("delete essence message failed")) - return deleteEssenceMsgResponse { } + return DeleteEssenceMsgResponse.newBuilder().build() } @Grpc("MessageService", "GetEssenceMessages") override suspend fun getEssenceMessages(request: GetEssenceMessagesRequest): GetEssenceMessagesResponse { val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString()) - return getEssenceMessagesResponse { + return GetEssenceMessagesResponse.newBuilder().apply { MessageHelper.getEssenceMessageList(request.groupId, request.page, request.pageSize).onFailure { throw StatusRuntimeException(Status.INTERNAL.withCause(it)) }.getOrThrow().forEach { - essenceMessage.add(essenceMessage { + addEssenceMessage(EssenceMessage.newBuilder().apply { withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) suspendCancellableCoroutine { continuation -> @@ -408,7 +339,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl this.jsonElements = it.messageContent.toString() }) } - } + }.build() } @Grpc("MessageService", "SetEssenceMessage") @@ -432,21 +363,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl if (MessageHelper.setEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null) { throw StatusRuntimeException(Status.NOT_FOUND.withDescription("set essence message failed")) } - return setEssenceMessageResponse { } + return SetEssenceMessageResponse.newBuilder().build() } @Grpc("MessageService", "SetMessageCommentEmoji") override suspend fun setMessageCommentEmoji(request: SetMessageCommentEmojiRequest): SetMessageCommentEmojiResponse { val contact = request.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } val msg: MsgRecord = withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) @@ -463,7 +396,12 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl } } } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) - MessageHelper.setGroupMessageCommentFace(request.contact.longPeer(), msg.msgSeq.toULong(), request.faceId.toString(), request.isComment) - return setMessageCommentEmojiResponse { } + MessageHelper.setGroupMessageCommentFace( + request.contact.longPeer(), + msg.msgSeq.toULong(), + request.faceId.toString(), + request.isComment + ) + return SetMessageCommentEmojiResponse.newBuilder().build() } } \ No newline at end of file diff --git a/xposed/src/main/java/kritor/service/WebService.kt b/xposed/src/main/java/kritor/service/WebService.kt index 81e3deff..83b62324 100644 --- a/xposed/src/main/java/kritor/service/WebService.kt +++ b/xposed/src/main/java/kritor/service/WebService.kt @@ -2,36 +2,24 @@ package kritor.service import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.web.GetCSRFTokenRequest -import io.kritor.web.GetCSRFTokenResponse -import io.kritor.web.GetCookiesRequest -import io.kritor.web.GetCookiesResponse -import io.kritor.web.GetCredentialsRequest -import io.kritor.web.GetCredentialsResponse -import io.kritor.web.GetHttpCookiesRequest -import io.kritor.web.GetHttpCookiesResponse -import io.kritor.web.WebServiceGrpcKt -import io.kritor.web.getCSRFTokenResponse -import io.kritor.web.getCookiesResponse -import io.kritor.web.getCredentialsResponse -import io.kritor.web.getHttpCookiesResponse +import io.kritor.web.* import qq.service.ticket.TicketHelper internal object WebService: WebServiceGrpcKt.WebServiceCoroutineImplBase() { @Grpc("WebService", "GetCookies") override suspend fun getCookies(request: GetCookiesRequest): GetCookiesResponse { - return getCookiesResponse { + return GetCookiesResponse.newBuilder().apply { if (request.domain.isNullOrEmpty()) { this.cookie = TicketHelper.getCookie() } else { this.cookie = TicketHelper.getCookie(request.domain) } - } + }.build() } @Grpc("WebService", "GetCredentials") override suspend fun getCredentials(request: GetCredentialsRequest): GetCredentialsResponse { - return getCredentialsResponse { + return GetCredentialsResponse.newBuilder().apply { if (request.domain.isNullOrEmpty()) { val uin = TicketHelper.getUin() val skey = TicketHelper.getRealSkey(uin) @@ -46,27 +34,25 @@ internal object WebService: WebServiceGrpcKt.WebServiceCoroutineImplBase() { this.cookie = "o_cookie=$uin; ied_qq=o$uin; pac_uid=1_$uin; uin=o$uin; skey=$skey; p_uin=o$uin; p_skey=$pskey; pt4_token=$pt4token;" this.bkn = TicketHelper.getCSRF(pskey) } - } + }.build() } @Grpc("WebService", "GetCSRFToken") override suspend fun getCSRFToken(request: GetCSRFTokenRequest): GetCSRFTokenResponse { - return getCSRFTokenResponse { + return GetCSRFTokenResponse.newBuilder().apply { if (request.domain.isNullOrEmpty()) { this.bkn = TicketHelper.getCSRF() } else { this.bkn = TicketHelper.getCSRF(TicketHelper.getUin(), request.domain) } - } + }.build() } @Grpc("WebService", "GetHttpCookies") override suspend fun getHttpCookies(request: GetHttpCookiesRequest): GetHttpCookiesResponse { - return getHttpCookiesResponse { + return GetHttpCookiesResponse.newBuilder().apply { this.cookie = TicketHelper.getHttpCookies(request.appid, request.daid, request.jumpUrl) ?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get http cookies")) - } + }.build() } - - } \ No newline at end of file diff --git a/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt b/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt index a90a273a..cbe0d624 100644 --- a/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt +++ b/xposed/src/main/java/moe/fuqiuluo/shamrock/internals/GlobalEventTransmitter.kt @@ -4,37 +4,9 @@ package moe.fuqiuluo.shamrock.internals import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgRecord -import io.kritor.event.GroupApplyType -import io.kritor.event.GroupMemberBanType -import io.kritor.event.GroupMemberDecreasedType -import io.kritor.event.GroupMemberIncreasedType -import io.kritor.event.MessageEvent -import io.kritor.event.NoticeEvent -import io.kritor.event.NoticeType -import io.kritor.event.RequestType -import io.kritor.event.RequestsEvent -import io.kritor.event.Scene -import io.kritor.event.contact -import io.kritor.event.essenceMessageNotice -import io.kritor.event.friendApplyRequest -import io.kritor.event.friendFileComeNotice -import io.kritor.event.friendPokeNotice -import io.kritor.event.friendRecallNotice -import io.kritor.event.groupAdminChangedNotice -import io.kritor.event.groupApplyRequest -import io.kritor.event.groupFileComeNotice -import io.kritor.event.groupMemberBannedNotice -import io.kritor.event.groupMemberDecreasedNotice -import io.kritor.event.groupMemberIncreasedNotice -import io.kritor.event.groupPokeNotice -import io.kritor.event.groupRecallNotice -import io.kritor.event.groupSignNotice -import io.kritor.event.groupUniqueTitleChangedNotice -import io.kritor.event.groupWholeBanNotice -import io.kritor.event.messageEvent -import io.kritor.event.noticeEvent -import io.kritor.event.requestsEvent -import io.kritor.event.sender +import io.kritor.event.* +import io.kritor.message.Contact +import io.kritor.message.Sender import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.flow.FlowCollector @@ -43,8 +15,8 @@ import kotlinx.coroutines.launch import qq.service.QQInterfaces import qq.service.msg.toKritorEventMessages -internal object GlobalEventTransmitter: QQInterfaces() { - private val messageEventFlow by lazy { +internal object GlobalEventTransmitter : QQInterfaces() { + private val MessageEventFlow by lazy { MutableSharedFlow>() } private val noticeEventFlow by lazy { @@ -58,30 +30,30 @@ internal object GlobalEventTransmitter: QQInterfaces() { private suspend fun pushRequest(requestEvent: RequestsEvent) = requestEventFlow.emit(requestEvent) - private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message) + private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = + MessageEventFlow.emit(record to message) object MessageTransmitter { suspend fun transGroupMessage( record: MsgRecord, elements: ArrayList, ): Boolean { - transMessageEvent(record, messageEvent { + transMessageEvent(record, MessageEvent.newBuilder().apply { this.time = record.msgTime.toInt() - this.scene = Scene.GROUP this.messageId = record.msgId this.messageSeq = record.msgSeq - this.contact = contact { + this.contact = Contact.newBuilder().apply { this.scene = scene this.peer = record.peerUin.toString() this.subPeer = record.peerUid - } - this.sender = sender { + }.build() + this.sender = Sender.newBuilder().apply { this.uin = record.senderUin this.uid = record.senderUid this.nick = record.sendNickName - } - this.elements.addAll(elements.toKritorEventMessages(record)) - }) + }.build() + this.addAllElements(elements.toKritorEventMessages(record)) + }.build()) return true } @@ -89,23 +61,22 @@ internal object GlobalEventTransmitter: QQInterfaces() { record: MsgRecord, elements: ArrayList, ): Boolean { - transMessageEvent(record, messageEvent { + transMessageEvent(record, MessageEvent.newBuilder().apply { this.time = record.msgTime.toInt() - this.scene = Scene.FRIEND this.messageId = record.msgId this.messageSeq = record.msgSeq - this.contact = contact { + this.contact = Contact.newBuilder().apply { this.scene = scene this.peer = record.senderUin.toString() this.subPeer = record.senderUid - } - this.sender = sender { + }.build() + this.sender = Sender.newBuilder().apply { this.uin = record.senderUin this.uid = record.senderUid this.nick = record.sendNickName - } - this.elements.addAll(elements.toKritorEventMessages(record)) - }) + }.build() + this.addAllElements(elements.toKritorEventMessages(record)) + }.build()) return true } @@ -115,23 +86,22 @@ internal object GlobalEventTransmitter: QQInterfaces() { groupCode: Long, fromNick: String, ): Boolean { - transMessageEvent(record, messageEvent { + transMessageEvent(record, MessageEvent.newBuilder().apply { this.time = record.msgTime.toInt() - this.scene = Scene.FRIEND this.messageId = record.msgId this.messageSeq = record.msgSeq - this.contact = contact { + this.contact = Contact.newBuilder().apply { this.scene = scene this.peer = record.senderUin.toString() this.subPeer = groupCode.toString() - } - this.sender = sender { + }.build() + this.sender = Sender.newBuilder().apply { this.uin = record.senderUin this.uid = record.senderUid this.nick = record.sendNickName - } - this.elements.addAll(elements.toKritorEventMessages(record)) - }) + }.build() + this.addAllElements(elements.toKritorEventMessages(record)) + }.build()) return true } @@ -139,23 +109,22 @@ internal object GlobalEventTransmitter: QQInterfaces() { record: MsgRecord, elements: ArrayList, ): Boolean { - transMessageEvent(record, messageEvent { + transMessageEvent(record, MessageEvent.newBuilder().apply { this.time = record.msgTime.toInt() - this.scene = Scene.GUILD this.messageId = record.msgId this.messageSeq = record.msgSeq - this.contact = contact { + this.contact = Contact.newBuilder().apply { this.scene = scene this.peer = record.guildId ?: "" this.subPeer = record.channelId ?: "" - } - this.sender = sender { + }.build() + this.sender = Sender.newBuilder().apply { this.uin = record.senderUin this.uid = record.senderUid this.nick = record.sendNickName - } - this.elements.addAll(elements.toKritorEventMessages(record)) - }) + }.build() + this.addAllElements(elements.toKritorEventMessages(record)) + }.build()) return true } } @@ -177,10 +146,10 @@ internal object GlobalEventTransmitter: QQInterfaces() { expireTime: Long, url: String ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.FRIEND_FILE_COME this.time = msgTime.toInt() - this.friendFileCome = friendFileComeNotice { + this.friendFileCome = FriendFileComeNotice.newBuilder().apply { this.fileId = fileId this.fileName = fileName this.operator = userId @@ -188,8 +157,8 @@ internal object GlobalEventTransmitter: QQInterfaces() { this.expireTime = expireTime.toInt() this.fileSubId = fileSubId this.url = url - } - }) + }.build() + }.build()) return true } @@ -206,10 +175,10 @@ internal object GlobalEventTransmitter: QQInterfaces() { bizId: Int, url: String ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_FILE_COME this.time = msgTime.toInt() - this.groupFileCome = groupFileComeNotice { + this.groupFileCome = GroupFileComeNotice.newBuilder().apply { this.groupId = groupId this.operator = userId this.fileId = uuid @@ -217,8 +186,8 @@ internal object GlobalEventTransmitter: QQInterfaces() { this.fileSize = fileSize this.biz = bizId this.url = url - } - }) + }.build() + }.build()) return true } } @@ -227,33 +196,47 @@ internal object GlobalEventTransmitter: QQInterfaces() { * 群聊通知 通知器 */ object GroupNoticeTransmitter { - suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean { - pushNotice(noticeEvent { + suspend fun transGroupSign( + time: Long, + target: Long, + action: String?, + rankImg: String?, + groupCode: Long + ): Boolean { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_SIGN this.time = time.toInt() - this.groupSign = groupSignNotice { + this.groupSign = GroupSignNotice.newBuilder().apply { this.groupId = groupCode this.targetUin = target this.action = action ?: "" this.suffix = "" this.rankImage = rankImg ?: "" - } - }) + }.build() + }.build()) return true } - suspend fun transGroupPoke(time: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean { - pushNotice(noticeEvent { + suspend fun transGroupPoke( + time: Long, + operator: Long, + target: Long, + action: String?, + suffix: String?, + actionImg: String?, + groupCode: Long + ): Boolean { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_POKE this.time = time.toInt() - this.groupPoke = groupPokeNotice { + this.groupPoke = GroupPokeNotice.newBuilder().apply { this.action = action ?: "" this.target = target this.operator = operator this.suffix = suffix ?: "" this.actionImage = actionImg ?: "" - } - }) + }.build() + }.build()) return true } @@ -266,18 +249,18 @@ internal object GlobalEventTransmitter: QQInterfaces() { operatorUid: String, type: GroupMemberIncreasedType ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_MEMBER_INCREASE this.time = time.toInt() - this.groupMemberIncrease = groupMemberIncreasedNotice { + this.groupMemberIncrease = GroupMemberIncreasedNotice.newBuilder().apply { this.groupId = groupCode this.operatorUid = operatorUid this.operatorUin = operator this.targetUid = targetUid this.targetUin = target this.type = type - } - }) + }.build() + }.build()) return true } @@ -290,18 +273,18 @@ internal object GlobalEventTransmitter: QQInterfaces() { operatorUid: String, type: GroupMemberDecreasedType ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_MEMBER_INCREASE this.time = time.toInt() - this.groupMemberDecrease = groupMemberDecreasedNotice { + this.groupMemberDecrease = GroupMemberDecreasedNotice.newBuilder().apply { this.groupId = groupCode this.operatorUid = operatorUid this.operatorUin = operator this.targetUid = targetUid this.targetUin = target this.type = type - } - }) + }.build() + }.build()) return true } @@ -312,16 +295,16 @@ internal object GlobalEventTransmitter: QQInterfaces() { groupCode: Long, setAdmin: Boolean ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_ADMIN_CHANGED this.time = msgTime.toInt() - this.groupAdminChanged = groupAdminChangedNotice { + this.groupAdminChanged = GroupAdminChangedNotice.newBuilder().apply { this.groupId = groupCode this.targetUid = targetUid this.targetUin = target this.isAdmin = setAdmin - } - }) + }.build() + }.build()) return true } @@ -331,15 +314,15 @@ internal object GlobalEventTransmitter: QQInterfaces() { groupCode: Long, isOpen: Boolean ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_WHOLE_BAN this.time = msgTime.toInt() - this.groupWholeBan = groupWholeBanNotice { + this.groupWholeBan = GroupWholeBanNotice.newBuilder().apply { this.groupId = groupCode this.isWholeBan = isOpen this.operator = operator - } - }) + }.build() + }.build()) return true } @@ -352,10 +335,10 @@ internal object GlobalEventTransmitter: QQInterfaces() { groupCode: Long, duration: Int ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_MEMBER_BANNED this.time = msgTime.toInt() - this.groupMemberBanned = groupMemberBannedNotice { + this.groupMemberBanned = GroupMemberBannedNotice.newBuilder().apply { this.groupId = groupCode this.operatorUid = operatorUid this.operatorUin = operator @@ -364,8 +347,8 @@ internal object GlobalEventTransmitter: QQInterfaces() { this.duration = duration this.type = if (duration > 0) GroupMemberBanType.BAN else GroupMemberBanType.LIFT_BAN - } - }) + }.build() + }.build()) return true } @@ -379,10 +362,10 @@ internal object GlobalEventTransmitter: QQInterfaces() { msgId: Long, tipText: String ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_RECALL this.time = time.toInt() - this.groupRecall = groupRecallNotice { + this.groupRecall = GroupRecallNotice.newBuilder().apply { this.groupId = groupCode this.operatorUid = operatorUid this.operatorUin = operator @@ -390,8 +373,8 @@ internal object GlobalEventTransmitter: QQInterfaces() { this.targetUin = target this.messageId = msgId this.tipText = tipText - } - }) + }.build() + }.build()) return true } @@ -412,15 +395,15 @@ internal object GlobalEventTransmitter: QQInterfaces() { title: String, groupId: Long ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_MEMBER_UNIQUE_TITLE_CHANGED this.time = time.toInt() - this.groupMemberUniqueTitleChanged = groupUniqueTitleChangedNotice { + this.groupMemberUniqueTitleChanged = GroupUniqueTitleChangedNotice.newBuilder().apply { this.groupId = groupId this.target = targetId this.title = title - } - }) + }.build() + }.build()) return true } @@ -432,17 +415,17 @@ internal object GlobalEventTransmitter: QQInterfaces() { groupId: Long, subType: UInt ): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.GROUP_ESSENCE_CHANGED this.time = time.toInt() - this.groupEssenceChanged = essenceMessageNotice { + this.groupEssenceChanged = EssenceMessageNotice.newBuilder().apply { this.groupId = groupId this.messageId = msgId this.sender = senderUin this.operator = operatorUin this.subType = subType.toInt() - } - }) + }.build() + }.build()) return true } } @@ -451,31 +434,38 @@ internal object GlobalEventTransmitter: QQInterfaces() { * 私聊通知 通知器 */ object PrivateNoticeTransmitter { - suspend fun transPrivatePoke(msgTime: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?): Boolean { - pushNotice(noticeEvent { + suspend fun transPrivatePoke( + msgTime: Long, + operator: Long, + target: Long, + action: String?, + suffix: String?, + actionImg: String? + ): Boolean { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.FRIEND_POKE this.time = msgTime.toInt() - this.friendPoke = friendPokeNotice { + this.friendPoke = FriendPokeNotice.newBuilder().apply { this.action = action ?: "" this.target = target this.operator = operator this.suffix = suffix ?: "" this.actionImage = actionImg ?: "" - } - }) + }.build() + }.build()) return true } suspend fun transPrivateRecall(time: Long, operator: Long, msgId: Long, tipText: String): Boolean { - pushNotice(noticeEvent { + pushNotice(NoticeEvent.newBuilder().apply { this.type = NoticeType.FRIEND_RECALL this.time = time.toInt() - this.friendRecall = friendRecallNotice { + this.friendRecall = FriendRecallNotice.newBuilder().apply { this.operator = operator this.messageId = msgId this.tipText = tipText - } - }) + }.build() + }.build()) return true } @@ -486,15 +476,15 @@ internal object GlobalEventTransmitter: QQInterfaces() { */ object RequestTransmitter { suspend fun transFriendApp(time: Long, operator: Long, tipText: String, flag: String): Boolean { - pushRequest(requestsEvent { + pushRequest(RequestsEvent.newBuilder().apply { this.type = RequestType.FRIEND_APPLY this.time = time.toInt() - this.friendApply = friendApplyRequest { + this.friendApply = FriendApplyRequest.newBuilder().apply { this.applierUin = operator this.message = tipText this.flag = flag - } - }) + }.build() + }.build()) return true } @@ -507,24 +497,24 @@ internal object GlobalEventTransmitter: QQInterfaces() { flag: String, type: GroupApplyType ): Boolean { - pushRequest(requestsEvent { + pushRequest(RequestsEvent.newBuilder().apply { this.type = RequestType.GROUP_APPLY this.time = time.toInt() - this.groupApply = groupApplyRequest { + this.groupApply = GroupApplyRequest.newBuilder().apply { this.applierUid = applierUid this.applierUin = applier this.groupId = groupCode this.reason = reason this.flag = flag this.type = type - } - }) + }.build() + }.build()) return true } } suspend inline fun onMessageEvent(collector: FlowCollector>) { - messageEventFlow.collect { + MessageEventFlow.collect { GlobalScope.launch { collector.emit(it) } diff --git a/xposed/src/main/java/qq/service/file/GroupFileHelper.kt b/xposed/src/main/java/qq/service/file/GroupFileHelper.kt index 61076ce1..c501e8ae 100644 --- a/xposed/src/main/java/qq/service/file/GroupFileHelper.kt +++ b/xposed/src/main/java/qq/service/file/GroupFileHelper.kt @@ -10,10 +10,6 @@ import io.kritor.file.Folder import io.kritor.file.GetFileSystemInfoResponse import io.kritor.file.GetFilesRequest import io.kritor.file.GetFilesResponse -import io.kritor.file.folder -import io.kritor.file.getFileSystemInfoResponse -import io.kritor.file.getFilesRequest -import io.kritor.file.getFilesResponse import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY @@ -73,12 +69,12 @@ internal object GroupFileHelper: QQInterfaces() { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response x2")) } - return getFileSystemInfoResponse { + return GetFileSystemInfoResponse.newBuilder().apply { this.fileCount = fileCnt this.totalCount = limitCnt this.totalSpace = totalSpace.toInt() this.usedSpace = usedSpace.toInt() - } + }.build() } suspend fun getGroupFiles(groupId: Long, folderId: String = "/"): GetFilesResponse { @@ -108,7 +104,7 @@ internal object GroupFileHelper: QQInterfaces() { throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed")) } val files = arrayListOf() - val dirs = arrayListOf() + val folders = arrayListOf() if (fromServiceMsg.wupBuffer != null) { val oidb = oidb_sso.OIDBSSOPkg().mergeFrom(fromServiceMsg.wupBuffer.slice(4).let { if (it[0] == 0x78.toByte()) DeflateTools.uncompress(it) else it @@ -119,7 +115,7 @@ internal object GroupFileHelper: QQInterfaces() { rpt_item_list.get().forEach { file -> if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FILE) { val fileInfo = file.file_info - files.add(io.kritor.file.file { + files.add(File.newBuilder().apply { this.fileId = fileInfo.str_file_id.get() this.fileName = fileInfo.str_file_name.get() this.fileSize = fileInfo.uint64_file_size.get() @@ -133,18 +129,18 @@ internal object GroupFileHelper: QQInterfaces() { this.sha = fileInfo.bytes_sha.get().toByteArray().toHexString() this.sha3 = fileInfo.bytes_sha3.get().toByteArray().toHexString() this.md5 = fileInfo.bytes_md5.get().toByteArray().toHexString() - }) + }.build()) } else if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FOLDER) { val folderInfo = file.folder_info - dirs.add(folder { + folders.add(Folder.newBuilder().apply { this.folderId = folderInfo.str_folder_id.get() this.folderName = folderInfo.str_folder_name.get() this.totalFileCount = folderInfo.uint32_total_file_count.get() this.createTime = folderInfo.uint32_create_time.get() this.creator = folderInfo.uint64_create_uin.get() this.creatorName = folderInfo.str_creator_name.get() - }) + }.build()) } else { LogCenter.log("未知文件类型: ${file.uint32_type.get()}", Level.WARN) } @@ -154,9 +150,9 @@ internal object GroupFileHelper: QQInterfaces() { throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response")) } - return getFilesResponse { - this.files.addAll(files) - this.folders.addAll(folders) - } + return GetFilesResponse.newBuilder().apply { + this.addAllFiles(files) + this.addAllFolders(folders) + }.build() } } \ No newline at end of file diff --git a/xposed/src/main/java/qq/service/msg/ForwardMessageHelper.kt b/xposed/src/main/java/qq/service/msg/ForwardMessageHelper.kt index eb1c8a46..6846ffef 100644 --- a/xposed/src/main/java/qq/service/msg/ForwardMessageHelper.kt +++ b/xposed/src/main/java/qq/service/msg/ForwardMessageHelper.kt @@ -6,14 +6,9 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.msg.api.IMsgService import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.message.Element -import io.kritor.message.ElementType import io.kritor.message.ForwardElement import io.kritor.message.ForwardMessageBody import io.kritor.message.Scene -import io.kritor.message.forwardElement -import io.kritor.message.nodeOrNull -import io.kritor.message.senderOrNull import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.shamrock.helper.Level @@ -28,12 +23,12 @@ import qq.service.QQInterfaces import qq.service.contact.ContactHelper import qq.service.msg.MessageHelper.getMultiMsg import qq.service.ticket.TicketHelper -import java.util.UUID +import java.util.* import kotlin.coroutines.resume import kotlin.random.Random import kotlin.time.Duration.Companion.seconds -internal object ForwardMessageHelper: QQInterfaces() { +internal object ForwardMessageHelper : QQInterfaces() { suspend fun uploadMultiMsg( chatType: Int, peerId: String, @@ -47,23 +42,26 @@ internal object ForwardMessageHelper: QQInterfaces() { val msgs = messages.mapNotNull { msg -> kotlin.runCatching { val contact = msg.contact.let { - MessageHelper.generateContact(when(it.scene!!) { - Scene.GROUP -> MsgConstant.KCHATTYPEGROUP - Scene.FRIEND -> MsgConstant.KCHATTYPEC2C - Scene.GUILD -> MsgConstant.KCHATTYPEGUILD - Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP - Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN - Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) - }, it.peer, it.subPeer) + MessageHelper.generateContact( + when (it.scene!!) { + Scene.GROUP -> MsgConstant.KCHATTYPEGROUP + Scene.FRIEND -> MsgConstant.KCHATTYPEC2C + Scene.GUILD -> MsgConstant.KCHATTYPEGUILD + Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP + Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN + Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) + }, it.peer, it.subPeer + ) } - val node = msg.elementsList.find { it.type == ElementType.NODE }?.nodeOrNull - if (node != null) { - val msgId = node.messageId + if (msg.hasMessageId()) { val record: MsgRecord = withTimeoutOrNull(5000) { val service = QRoute.api(IMsgService::class.java) suspendCancellableCoroutine { continuation -> - service.getMsgsByMsgId(contact, arrayListOf(msgId)) { code, _, msgRecords -> + service.getMsgsByMsgId( + contact, + arrayListOf(msg.messageId.toLong()) + ) { code, _, msgRecords -> if (code == 0 && msgRecords.isNotEmpty()) { continuation.resume(msgRecords.first()) } else { @@ -74,7 +72,7 @@ internal object ForwardMessageHelper: QQInterfaces() { continuation.resume(null) } } - } ?: error("合并转发消息节点消息(id = $msgId)获取失败") + } ?: error("合并转发消息节点消息(id = ${msg.messageId})获取失败") PushMsgBody( msgHead = ResponseHead( peerUid = record.senderUid, @@ -121,12 +119,19 @@ internal object ForwardMessageHelper: QQInterfaces() { ) } else { PushMsgBody( - msgHead = ResponseHead( - peer = msg.senderOrNull?.uin ?: TicketHelper.getUin().toLong(), - peerUid = msg.senderOrNull?.uid ?: TicketHelper.getUid(), + msgHead = if (msg.hasSender()) ResponseHead( + peer = if (msg.sender.hasUin()) msg.sender.uin else TicketHelper.getUin().toLong(), + peerUid = msg.sender.uid, receiverUid = TicketHelper.getUid(), forward = ResponseForward( - friendName = msg.senderOrNull?.nick ?: TicketHelper.getNickname() + friendName = if (msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname() + ) + ) else ResponseHead( + peer = TicketHelper.getUin().toLong(), + peerUid = TicketHelper.getUid(), + receiverUid = TicketHelper.getUid(), + forward = ResponseForward( + friendName = TicketHelper.getNickname() ) ), contentHead = ContentHead( @@ -150,7 +155,8 @@ internal object ForwardMessageHelper: QQInterfaces() { ), body = MsgBody( richText = msg.elementsList.toRichText(contact).onSuccess { - desc[++i] = (msg.senderOrNull?.nick ?: TicketHelper.getNickname()) + ": " + it.first + desc[++i] = + (if (msg.hasSender() && msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname()) + ": " + it.first }.onFailure { error("消息合成失败: ${it.stackTraceToString()}") }.getOrThrow().second @@ -189,15 +195,17 @@ internal object ForwardMessageHelper: QQInterfaces() { sendInfo = when (chatType) { MsgConstant.KCHATTYPEC2C -> SendLongMsgInfo( type = 1, - uid = LongMsgUid(if(peerId.startsWith("u_")) peerId else ContactHelper.getUidByUinAsync(peerId.toLong()) ), + uid = LongMsgUid(if (peerId.startsWith("u_")) peerId else ContactHelper.getUidByUinAsync(peerId.toLong())), payload = DeflateTools.gzip(payload.toByteArray()) ) + MsgConstant.KCHATTYPEGROUP -> SendLongMsgInfo( type = 3, uid = LongMsgUid(fromId), groupUin = fromId.toULong(), payload = DeflateTools.gzip(payload.toByteArray()) ) + else -> throw UnsupportedOperationException("Unsupported chatType: $chatType") }, setting = LongMsgSettings( @@ -208,8 +216,9 @@ internal object ForwardMessageHelper: QQInterfaces() { ) ).toByteArray() - val fromServiceMsg = sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 60.seconds) - ?: return Result.failure(Exception("unable to upload multi message, response timeout")) + val fromServiceMsg = + sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 60.seconds) + ?: return Result.failure(Exception("unable to upload multi message, response timeout")) val rsp = runCatching { fromServiceMsg.wupBuffer.slice(4).decodeProtobuf() }.getOrElse { @@ -217,11 +226,11 @@ internal object ForwardMessageHelper: QQInterfaces() { } val resId = rsp.sendResult?.resId ?: return Result.failure(Exception("unable to upload multi message")) - return Result.success(forwardElement { + return Result.success(ForwardElement.newBuilder().apply { this.id = resId this.summary = summary this.uniseq = UUID.randomUUID().toString() this.description = desc.slice(0..if (i < 3) i else 3).joinToString("\n") - }) + }.build()) } } \ No newline at end of file diff --git a/xposed/src/main/java/qq/service/msg/MsgConvertor.kt b/xposed/src/main/java/qq/service/msg/MsgConvertor.kt index aa2e277a..17dd5000 100644 --- a/xposed/src/main/java/qq/service/msg/MsgConvertor.kt +++ b/xposed/src/main/java/qq/service/msg/MsgConvertor.kt @@ -5,27 +5,7 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.msg.api.IMsgService -import io.kritor.event.Element -import io.kritor.event.ImageType -import io.kritor.event.Scene -import io.kritor.event.atElement -import io.kritor.event.basketballElement -import io.kritor.event.buttonAction -import io.kritor.event.buttonActionPermission -import io.kritor.event.buttonRender -import io.kritor.event.contactElement -import io.kritor.event.diceElement -import io.kritor.event.faceElement -import io.kritor.event.forwardElement -import io.kritor.event.imageElement -import io.kritor.event.jsonElement -import io.kritor.event.locationElement -import io.kritor.event.pokeElement -import io.kritor.event.replyElement -import io.kritor.event.rpsElement -import io.kritor.event.textElement -import io.kritor.event.videoElement -import io.kritor.event.voiceElement +import io.kritor.message.* import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.withTimeoutOrNull import moe.fuqiuluo.shamrock.helper.ActionMsgException @@ -75,12 +55,12 @@ private object MsgConvertor { val text = element.textElement val elem = Element.newBuilder() if (text.atType != MsgConstant.ATTYPEUNKNOWN) { - elem.setAt(atElement { + elem.setAt(AtElement.newBuilder().apply { this.uid = text.atNtUid this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong() }) } else { - elem.setText(textElement { + elem.setText(TextElement.newBuilder().apply { this.text = text.content }) } @@ -91,28 +71,32 @@ private object MsgConvertor { val face = element.faceElement val elem = Element.newBuilder() if (face.faceType == 5) { - elem.setPoke(pokeElement { + elem.setPoke(PokeElement.newBuilder().apply { this.id = face.vaspokeId this.type = face.pokeType this.strength = face.pokeStrength }) } else { - when(face.faceIndex) { - 114 -> elem.setBasketball(basketballElement { + when (face.faceIndex) { + 114 -> elem.setBasketball(BasketballElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 358 -> elem.setDice(diceElement { + + 358 -> elem.setDice(DiceElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 359 -> elem.setRps(rpsElement { + + 359 -> elem.setRps(RpsElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 394 -> elem.setFace(faceElement { + + 394 -> elem.setFace(FaceElement.newBuilder().apply { this.id = face.faceIndex this.isBig = face.faceType == 3 this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1 }) - else -> elem.setFace(faceElement { + + else -> elem.setFace(FaceElement.newBuilder().apply { this.id = face.faceIndex this.isBig = face.faceType == 3 }) @@ -150,7 +134,7 @@ private object MsgConvertor { LogCenter.log({ "receive image: $image" }, Level.DEBUG) val elem = Element.newBuilder() - elem.setImage(imageElement { + elem.setImage(ImageElement.newBuilder().apply { this.file = md5 this.url = when (record.chatType) { MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( @@ -190,7 +174,8 @@ private object MsgConvertor { else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}") } - this.type = if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON + this.type = + if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON this.subType = image.picSubType }) @@ -205,10 +190,14 @@ private object MsgConvertor { ptt.fileName.substring(5) else ptt.md5HexStr - elem.setVoice(voiceElement { + elem.setVoice(VoiceElement.newBuilder().apply { this.url = when (record.chatType) { MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid) - MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl("0", md5.hex2ByteArray(), ptt.fileUuid) + MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl( + "0", + md5.hex2ByteArray(), + ptt.fileUuid + ) else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}") } @@ -229,7 +218,7 @@ private object MsgConvertor { it[it.size - 2].hex2ByteArray() } } else video.fileName.split(".")[0].hex2ByteArray() - elem.setVideo(videoElement { + elem.setVideo(VideoElement.newBuilder().apply { this.file = md5.toHexString() this.url = when (record.chatType) { MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) @@ -244,7 +233,7 @@ private object MsgConvertor { suspend fun convertMarketFace(record: MsgRecord, element: MsgElement): Result { val marketFace = element.marketFaceElement val elem = Element.newBuilder() - elem.setMarketFace(io.kritor.event.marketFaceElement { + elem.setMarketFace(MarketFaceElement.newBuilder().apply { this.id = marketFace.emojiId.lowercase() }) return Result.success(elem.build()) @@ -256,8 +245,8 @@ private object MsgConvertor { when (data["app"].asString) { "com.tencent.multimsg" -> { val info = data["meta"].asJsonObject["detail"].asJsonObject - elem.setForward(forwardElement { - this.id = info["resid"].asString + elem.setForward(ForwardElement.newBuilder().apply { + this.resId = info["resid"].asString this.uniseq = info["uniseq"].asString this.summary = info["summary"].asString this.description = info["news"].asJsonArray.joinToString("\n") { @@ -268,7 +257,7 @@ private object MsgConvertor { "com.tencent.troopsharecard" -> { val info = data["meta"].asJsonObject["contact"].asJsonObject - elem.setContact(contactElement { + elem.setContact(ContactElement.newBuilder().apply { this.scene = Scene.GROUP this.peer = info["jumpUrl"].asString.split("group_code=")[1] }) @@ -276,7 +265,7 @@ private object MsgConvertor { "com.tencent.contact.lua" -> { val info = data["meta"].asJsonObject["contact"].asJsonObject - elem.setContact(contactElement { + elem.setContact(ContactElement.newBuilder().apply { this.scene = Scene.FRIEND this.peer = info["jumpUrl"].asString.split("uin=")[1] }) @@ -284,7 +273,7 @@ private object MsgConvertor { "com.tencent.map" -> { val info = data["meta"].asJsonObject["Location.Search"].asJsonObject - elem.setLocation(locationElement { + elem.setLocation(LocationElement.newBuilder().apply { this.lat = info["lat"].asString.toFloat() this.lon = info["lng"].asString.toFloat() this.address = info["address"].asString @@ -292,7 +281,7 @@ private object MsgConvertor { }) } - else -> elem.setJson(jsonElement { + else -> elem.setJson(JsonElement.newBuilder().apply { this.json = data.toString() }) } @@ -302,14 +291,15 @@ private object MsgConvertor { suspend fun convertReply(record: MsgRecord, element: MsgElement): Result { val reply = element.replyElement val elem = Element.newBuilder() - elem.setReply(replyElement { + elem.setReply(ReplyElement.newBuilder().apply { val msgSeq = reply.replayMsgSeq val contact = MessageHelper.generateContact(record) val sourceRecords = withTimeoutOrNull(3000) { suspendCancellableCoroutine { - QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records -> - it.resume(records) - } + QRoute.api(IMsgService::class.java) + .getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records -> + it.resume(records) + } } } if (sourceRecords.isNullOrEmpty()) { @@ -332,11 +322,17 @@ private object MsgConvertor { val fileSubId = fileMsg.fileSubId ?: "" val url = when (record.chatType) { MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId) - MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(record.guildId, record.channelId, fileId, bizId) + MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl( + record.guildId, + record.channelId, + fileId, + bizId + ) + else -> RichProtoSvc.getGroupFileDownUrl(record.peerUin, fileId, bizId) } val elem = Element.newBuilder() - elem.setFile(io.kritor.event.fileElement { + elem.setFile(FileElement.newBuilder().apply { this.name = fileName this.size = fileSize this.url = url @@ -351,7 +347,7 @@ private object MsgConvertor { suspend fun convertMarkdown(record: MsgRecord, element: MsgElement): Result { val markdown = element.markdownElement val elem = Element.newBuilder() - elem.setMarkdown(io.kritor.event.markdownElement { + elem.setMarkdown(MarkdownElement.newBuilder().apply { this.markdown = markdown.content }) return Result.success(elem.build()) @@ -360,7 +356,7 @@ private object MsgConvertor { suspend fun convertBubbleFace(record: MsgRecord, element: MsgElement): Result { val bubbleFace = element.faceBubbleElement val elem = Element.newBuilder() - elem.setBubbleFace(io.kritor.event.bubbleFaceElement { + elem.setBubbleFace(BubbleFaceElement.newBuilder().apply { this.id = bubbleFace.yellowFaceInfo.index this.count = bubbleFace.faceCount ?: 1 }) @@ -370,34 +366,34 @@ private object MsgConvertor { suspend fun convertInlineKeyboard(record: MsgRecord, element: MsgElement): Result { val inlineKeyboard = element.inlineKeyboardElement val elem = Element.newBuilder() - elem.setButton(io.kritor.event.buttonElement { + elem.setButton(ButtonElement.newBuilder().apply { inlineKeyboard.rows.forEach { row -> - this.rows.add(io.kritor.event.row { - row.buttons.forEach buttonsLoop@ { button -> + this.addRows(ButtonRow.newBuilder().apply { + row.buttons.forEach buttonsLoop@{ button -> if (button == null) return@buttonsLoop - this.buttons.add(io.kritor.event.button { + this.addButtons(Button.newBuilder().apply { this.id = button.id - this.action = buttonAction { + this.action = ButtonAction.newBuilder().apply { this.type = button.type - this.permission = buttonActionPermission { + this.permission = ButtonActionPermission.newBuilder().apply { this.type = button.permissionType button.specifyRoleIds?.let { - this.roleIds.addAll(it) + this.addAllRoleIds(it) } button.specifyTinyids?.let { - this.userIds.addAll(it) + this.addAllUserIds(it) } - } + }.build() this.unsupportedTips = button.unsupportTips ?: "" this.data = button.data ?: "" this.reply = button.isReply this.enter = button.enter - } - this.renderData = buttonRender { + }.build() + this.renderData = ButtonRender.newBuilder().apply { this.label = button.label ?: "" this.visitedLabel = button.visitedLabel ?: "" this.style = button.style - } + }.build() }) } }) diff --git a/xposed/src/main/java/qq/service/msg/MultiConvertor.kt b/xposed/src/main/java/qq/service/msg/MultiConvertor.kt index 44b9bbd9..bcb8b06f 100644 --- a/xposed/src/main/java/qq/service/msg/MultiConvertor.kt +++ b/xposed/src/main/java/qq/service/msg/MultiConvertor.kt @@ -1,24 +1,10 @@ @file:OptIn(ExperimentalUnsignedTypes::class) + package qq.service.msg import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.MsgConstant -import io.kritor.message.Element -import io.kritor.message.ElementType -import io.kritor.message.ImageType -import io.kritor.message.Scene -import io.kritor.message.atElement -import io.kritor.message.buttonActionPermission -import io.kritor.message.buttonElement -import io.kritor.message.contactElement -import io.kritor.message.faceElement -import io.kritor.message.forwardElement -import io.kritor.message.imageElement -import io.kritor.message.jsonElement -import io.kritor.message.locationElement -import io.kritor.message.markdownElement -import io.kritor.message.replyElement -import io.kritor.message.textElement +import io.kritor.message.* import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact import kotlinx.io.core.readUInt @@ -48,142 +34,151 @@ suspend fun List.toKritorResponseMessages(contact: Contact): ArrayList RichProtoSvc.getGroupPicDownUrl(origUrl, md5) + MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( + origUrl, + md5 + ) + MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(origUrl, md5) MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(origUrl, md5) else -> throw UnsupportedOperationException("Not supported chat type: $contact") } - } - }) + }.build() + }.build()) } else if (element.notOnlineImage != null) { - require(element.notOnlineImage != null) val md5 = element.notOnlineImage!!.picMd5.toHexString() val origUrl = element.notOnlineImage!!.origUrl!! - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.IMAGE - this.image = imageElement { + this.image = ImageElement.newBuilder().apply { this.fileName = md5 this.type = if (element.notOnlineImage?.original == true) ImageType.ORIGIN else ImageType.COMMON this.url = when (contact.chatType) { - MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(origUrl, md5) + MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( + origUrl, + md5 + ) + MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(origUrl, md5) MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(origUrl, md5) else -> throw UnsupportedOperationException("Not supported chat type: $contact") } - } - }) + }.build() + }.build()) } else if (element.generalFlags != null) { - val generalFlags = element.generalFlags!! - if (generalFlags.longTextFlag == 1u) { - kritorMessages.add(io.kritor.message.element { - this.type = ElementType.FORWARD - this.forward = forwardElement { - this.id = generalFlags.longTextResid ?: "" - } - }) - } +// val generalFlags = element.generalFlags!! +// if (generalFlags.longTextFlag == 1u) { +// kritorMessages.add(Element.newBuilder().apply { +// this.type = ElementType.FORWARD +// this.forward = forwardElement { +// this.id = generalFlags.longTextResid ?: "" +// } +// }) +// } } else if (element.srcMsg != null) { val srcMsg = element.srcMsg!! val msgId = srcMsg.pbReserve?.msgRand?.toLong() ?: 0 - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.REPLY - this.reply = replyElement { + this.reply = ReplyElement.newBuilder().apply { this.messageId = msgId - } - }) + }.build() + }.build()) } else if (element.lightApp != null) { val data = element.lightApp!!.data!! - val jsonStr = (if (data[0].toInt() == 1) DeflateTools.uncompress(data.slice(1)) else data.slice(1)).decodeToString() + val jsonStr = + (if (data[0].toInt() == 1) DeflateTools.uncompress(data.slice(1)) else data.slice(1)).decodeToString() val json = jsonStr.asJsonObject when (json["app"].asString) { "com.tencent.multimsg" -> { val info = json["meta"].asJsonObject["detail"].asJsonObject - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.FORWARD - this.forward = forwardElement { - this.id = info["resid"].asString + this.forward = ForwardElement.newBuilder().apply { + this.resId = info["resid"].asString this.uniseq = info["uniseq"].asString this.summary = info["summary"].asString this.description = info["news"].asJsonArray.joinToString("\n") { it.asJsonObject["text"].asString } - } - }) + }.build() + }.build()) } "com.tencent.troopsharecard" -> { val info = json["meta"].asJsonObject["contact"].asJsonObject - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.CONTACT - this.contact = contactElement { + this.contact = ContactElement.newBuilder().apply { this.scene = Scene.GROUP this.peer = info["jumpUrl"].asString.split("group_code=")[1] - } - }) + }.build() + }.build()) } "com.tencent.contact.lua" -> { val info = json["meta"].asJsonObject["contact"].asJsonObject - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.CONTACT - this.contact = contactElement { + this.contact = ContactElement.newBuilder().apply { this.scene = Scene.FRIEND this.peer = info["jumpUrl"].asString.split("uin=")[1] - } - }) + }.build() + }.build()) } "com.tencent.map" -> { val info = json["meta"].asJsonObject["Location.Search"].asJsonObject - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.LOCATION - this.location = locationElement { + this.location = LocationElement.newBuilder().apply { this.lat = info["lat"].asString.toFloat() this.lon = info["lng"].asString.toFloat() this.address = info["address"].asString this.title = info["name"].asString - } - }) + }.build() + }.build()) } + else -> { - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.JSON - this.json = jsonElement { + this.json = JsonElement.newBuilder().apply { this.json = jsonStr - } - }) + }.build() + }.build()) } } } else if (element.commonElem != null) { @@ -192,81 +187,78 @@ suspend fun List.toKritorResponseMessages(contact: Contact): ArrayList { val qFaceExtra = commonElem.elem!!.decodeProtobuf() when (qFaceExtra.faceId) { - 358 -> kritorMessages.add(io.kritor.message.element { + 358 -> kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.DICE - this.dice = io.kritor.message.diceElement { + this.dice = DiceElement.newBuilder().apply { this.id = qFaceExtra.result!!.toInt() - } - }) + }.build() + }.build()) - 359 -> kritorMessages.add(io.kritor.message.element { + 359 -> kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.RPS - this.rps = io.kritor.message.rpsElement { + this.rps = RpsElement.newBuilder().apply { this.id = qFaceExtra.result!!.toInt() - } - }) + }.build() + }.build()) - else -> kritorMessages.add(io.kritor.message.element { + else -> kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.FACE - this.face = faceElement { + this.face = FaceElement.newBuilder().apply { this.id = qFaceExtra.faceId ?: 0 this.isBig = false this.result = qFaceExtra.result?.toInt() ?: 0 - } - }) + }.build() + }.build()) } } 45 -> { val markdownExtra = commonElem.elem!!.decodeProtobuf() - kritorMessages.add(io.kritor.message.element { + kritorMessages.add(Element.newBuilder().apply { this.type = ElementType.MARKDOWN - this.markdown = markdownElement { + this.markdown = MarkdownElement.newBuilder().apply { this.markdown = markdownExtra.content!! - } - }) + }.build() + }.build()) } 46 -> { val buttonExtra = commonElem.elem!!.decodeProtobuf() - kritorMessages.add(io.kritor.message.element { - this.type = ElementType.BUTTON - this.button = buttonElement { - buttonExtra.field1!!.rows?.forEach { row -> - this.rows.add(io.kritor.message.row { - row.buttons?.forEach { button -> - this.buttons.add(io.kritor.message.button { - val renderData = button.renderData - val action = button.action - val permission = action?.permission - this.id = button.id ?: "" - this.renderData = io.kritor.message.buttonRender { - this.label = renderData?.label ?: "" - this.visitedLabel = renderData?.visitedLabel ?: "" - this.style = renderData?.style ?: 0 - } - this.action = io.kritor.message.buttonAction { - this.type = action?.type ?: 0 - this.permission = buttonActionPermission { - this.type = permission?.type ?: 0 - this.roleIds.addAll( - permission?.specifyRoleIds ?: emptyList() - ) - this.userIds.addAll( - permission?.specifyUserIds ?: emptyList() - ) - } - this.unsupportedTips = action?.unsupportTips ?: "" - this.data = action?.data ?: "" - this.reply = action?.reply ?: false - this.enter = action?.enter ?: false - } - }) - } - }) - } - } - }) + kritorMessages.add( + Element.newBuilder().setButton(ButtonElement.newBuilder().apply { + this.addAllRows(buttonExtra.field1!!.rows!!.map { row -> + ButtonRow.newBuilder().apply { + this.addAllButtons(row.buttons!!.map { button -> + Button.newBuilder().apply { + this.id = button.id + this.renderData = ButtonRender.newBuilder().apply { + this.label = button.renderData?.label ?: "" + this.visitedLabel = button.renderData?.visitedLabel ?: "" + this.style = button.renderData?.style ?: 0 + }.build() + this.action = ButtonAction.newBuilder().apply { + this.type = button.action?.type?:0 + this.permission = ButtonActionPermission.newBuilder().apply { + this.type = button.action?.permission?.type?:0 + button.action?.permission?.specifyRoleIds?.let { + this.addAllRoleIds(it) + } + button.action?.permission?.specifyUserIds?.let { + this.addAllUserIds(it) + } + }.build() + this.unsupportedTips = button.action?.unsupportTips ?: "" + this.data = button.action?.data ?: "" + this.reply = button.action?.reply ?: false + this.enter = button.action?.enter ?: false + }.build() + }.build() + }) + }.build() + }) + this.applicationId = buttonExtra.field1?.appid?.toLong() ?: 0L + }.build()).build() + ) } } } diff --git a/xposed/src/main/java/qq/service/msg/ReqMessageConvertor.kt b/xposed/src/main/java/qq/service/msg/ReqMessageConvertor.kt index 8636a54f..cd9ae043 100644 --- a/xposed/src/main/java/qq/service/msg/ReqMessageConvertor.kt +++ b/xposed/src/main/java/qq/service/msg/ReqMessageConvertor.kt @@ -1,8 +1,9 @@ package qq.service.msg import com.tencent.mobileqq.qroute.QRoute -import com.tencent.qqnt.kernel.nativeinterface.* import com.tencent.qqnt.kernel.nativeinterface.Contact +import com.tencent.qqnt.kernel.nativeinterface.MsgConstant +import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.msg.api.IMsgService import io.kritor.message.* import kotlinx.coroutines.suspendCancellableCoroutine @@ -54,12 +55,12 @@ private object ReqMsgConvertor { val text = element.textElement val elem = Element.newBuilder() if (text.atType != MsgConstant.ATTYPEUNKNOWN) { - elem.setAt(atElement { + elem.setAt(AtElement.newBuilder().apply { this.uid = text.atNtUid this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong() }) } else { - elem.setText(textElement { + elem.setText(TextElement.newBuilder().apply { this.text = text.content }) } @@ -70,28 +71,32 @@ private object ReqMsgConvertor { val face = element.faceElement val elem = Element.newBuilder() if (face.faceType == 5) { - elem.setPoke(pokeElement { + elem.setPoke(PokeElement.newBuilder().apply { this.id = face.vaspokeId this.type = face.pokeType this.strength = face.pokeStrength }) } else { - when(face.faceIndex) { - 114 -> elem.setBasketball(basketballElement { + when (face.faceIndex) { + 114 -> elem.setBasketball(BasketballElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 358 -> elem.setDice(diceElement { + + 358 -> elem.setDice(DiceElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 359 -> elem.setRps(rpsElement { + + 359 -> elem.setRps(RpsElement.newBuilder().apply { this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0 }) - 394 -> elem.setFace(faceElement { + + 394 -> elem.setFace(FaceElement.newBuilder().apply { this.id = face.faceIndex this.isBig = face.faceType == 3 this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1 }) - else -> elem.setFace(faceElement { + + else -> elem.setFace(FaceElement.newBuilder().apply { this.id = face.faceIndex this.isBig = face.faceType == 3 }) @@ -129,7 +134,7 @@ private object ReqMsgConvertor { LogCenter.log({ "receive image: $image" }, Level.DEBUG) val elem = Element.newBuilder() - elem.setImage(imageElement { + elem.setImage(ImageElement.newBuilder().apply { this.file = md5 this.url = when (contact.chatType) { MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( @@ -164,12 +169,13 @@ private object ReqMsgConvertor { sha = "", fileSize = image.fileSize.toULong(), peer = contact.longPeer().toString(), - subPeer ="0" + subPeer = "0" ) else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}") } - this.type = if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON + this.type = + if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON this.subType = image.picSubType }) @@ -184,10 +190,14 @@ private object ReqMsgConvertor { ptt.fileName.substring(5) else ptt.md5HexStr - elem.setVoice(voiceElement { + elem.setVoice(VoiceElement.newBuilder().apply { this.url = when (contact.chatType) { MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid) - MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl("0", md5.hex2ByteArray(), ptt.fileUuid) + MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl( + "0", + md5.hex2ByteArray(), + ptt.fileUuid + ) else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}") } @@ -208,7 +218,7 @@ private object ReqMsgConvertor { it[it.size - 2].hex2ByteArray() } } else video.fileName.split(".")[0].hex2ByteArray() - elem.setVideo(videoElement { + elem.setVideo(VideoElement.newBuilder().apply { this.file = md5.toHexString() this.url = when (contact.chatType) { MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) @@ -232,8 +242,8 @@ private object ReqMsgConvertor { when (data["app"].asString) { "com.tencent.multimsg" -> { val info = data["meta"].asJsonObject["detail"].asJsonObject - elem.setForward(forwardElement { - this.id = info["resid"].asString + elem.setForward(ForwardElement.newBuilder().apply { + this.resId = info["resid"].asString this.uniseq = info["uniseq"].asString this.summary = info["summary"].asString this.description = info["news"].asJsonArray.joinToString("\n") { @@ -244,7 +254,7 @@ private object ReqMsgConvertor { "com.tencent.troopsharecard" -> { val info = data["meta"].asJsonObject["contact"].asJsonObject - elem.setContact(contactElement { + elem.setContact(ContactElement.newBuilder().apply { this.scene = Scene.GROUP this.peer = info["jumpUrl"].asString.split("group_code=")[1] }) @@ -252,7 +262,7 @@ private object ReqMsgConvertor { "com.tencent.contact.lua" -> { val info = data["meta"].asJsonObject["contact"].asJsonObject - elem.setContact(contactElement { + elem.setContact(ContactElement.newBuilder().apply { this.scene = Scene.FRIEND this.peer = info["jumpUrl"].asString.split("uin=")[1] }) @@ -260,7 +270,7 @@ private object ReqMsgConvertor { "com.tencent.map" -> { val info = data["meta"].asJsonObject["Location.Search"].asJsonObject - elem.setLocation(locationElement { + elem.setLocation(LocationElement.newBuilder().apply { this.lat = info["lat"].asString.toFloat() this.lon = info["lng"].asString.toFloat() this.address = info["address"].asString @@ -268,7 +278,7 @@ private object ReqMsgConvertor { }) } - else -> elem.setJson(jsonElement { + else -> elem.setJson(JsonElement.newBuilder().apply { this.json = data.toString() }) } @@ -278,13 +288,14 @@ private object ReqMsgConvertor { suspend fun convertReply(contact: Contact, element: MsgElement): Result { val reply = element.replyElement val elem = Element.newBuilder() - elem.setReply(replyElement { + elem.setReply(ReplyElement.newBuilder().apply { val msgSeq = reply.replayMsgSeq val sourceRecords = withTimeoutOrNull(3000) { suspendCancellableCoroutine { - QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records -> - it.resume(records) - } + QRoute.api(IMsgService::class.java) + .getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records -> + it.resume(records) + } } } if (sourceRecords.isNullOrEmpty()) { @@ -307,11 +318,17 @@ private object ReqMsgConvertor { val fileSubId = fileMsg.fileSubId ?: "" val url = when (contact.chatType) { MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId) - MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(contact.guildId, contact.longPeer().toString(), fileId, bizId) + MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl( + contact.guildId, + contact.longPeer().toString(), + fileId, + bizId + ) + else -> RichProtoSvc.getGroupFileDownUrl(contact.longPeer(), fileId, bizId) } val elem = Element.newBuilder() - elem.setFile(fileElement { + elem.setFile(FileElement.newBuilder().apply { this.name = fileName this.size = fileSize this.url = url @@ -326,7 +343,7 @@ private object ReqMsgConvertor { suspend fun convertMarkdown(contact: Contact, element: MsgElement): Result { val markdown = element.markdownElement val elem = Element.newBuilder() - elem.setMarkdown(markdownElement { + elem.setMarkdown(MarkdownElement.newBuilder().apply { this.markdown = markdown.content }) return Result.success(elem.build()) @@ -335,7 +352,7 @@ private object ReqMsgConvertor { suspend fun convertBubbleFace(contact: Contact, element: MsgElement): Result { val bubbleFace = element.faceBubbleElement val elem = Element.newBuilder() - elem.setBubbleFace(bubbleFaceElement { + elem.setBubbleFace(BubbleFaceElement.newBuilder().apply { this.id = bubbleFace.yellowFaceInfo.index this.count = bubbleFace.faceCount ?: 1 }) @@ -345,38 +362,38 @@ private object ReqMsgConvertor { suspend fun convertInlineKeyboard(contact: Contact, element: MsgElement): Result { val inlineKeyboard = element.inlineKeyboardElement val elem = Element.newBuilder() - elem.setButton(buttonElement { - inlineKeyboard.rows.forEach { row -> - this.rows.add(row { - row.buttons.forEach buttonsLoop@ { button -> - if (button == null) return@buttonsLoop - this.buttons.add(button { + elem.setButton(ButtonElement.newBuilder().apply { + this.addAllRows(inlineKeyboard.rows.map { row -> + ButtonRow.newBuilder().apply { + this.addAllButtons(row.buttons.map { button -> + Button.newBuilder().apply { this.id = button.id - this.action = buttonAction { + this.renderData = ButtonRender.newBuilder().apply { + this.label = button.label ?: "" + this.visitedLabel = button.visitedLabel ?: "" + this.style = button.style + }.build() + this.action = ButtonAction.newBuilder().apply { this.type = button.type - this.permission = buttonActionPermission { + this.permission = ButtonActionPermission.newBuilder().apply { this.type = button.permissionType button.specifyRoleIds?.let { - this.roleIds.addAll(it) + this.addAllRoleIds(it) } button.specifyTinyids?.let { - this.userIds.addAll(it) + this.addAllUserIds(it) } - } + }.build() this.unsupportedTips = button.unsupportTips ?: "" this.data = button.data ?: "" this.reply = button.isReply this.enter = button.enter - } - this.renderData = buttonRender { - this.label = button.label ?: "" - this.visitedLabel = button.visitedLabel ?: "" - this.style = button.style - } - }) - } - }) - } + }.build() + }.build() + }) + }.build() + }) + this.applicationId = inlineKeyboard.botAppid }) return Result.success(elem.build()) } diff --git a/xposed/src/main/java/qq/service/msg/ReqMultiConvertor.kt b/xposed/src/main/java/qq/service/msg/ReqMultiConvertor.kt index 06d8fa78..6a07a4d0 100644 --- a/xposed/src/main/java/qq/service/msg/ReqMultiConvertor.kt +++ b/xposed/src/main/java/qq/service/msg/ReqMultiConvertor.kt @@ -443,7 +443,7 @@ suspend fun List.toRichText(contact: Contact): Result throw UnsupportedOperationException("Unsupported ElementType.GIFT") ElementType.MARKET_FACE -> throw UnsupportedOperationException("Unsupported ElementType.MARKET_FACE") ElementType.FORWARD -> { - val resId = it.forward.id + val resId = it.forward.resId val filename = UUID.randomUUID().toString().uppercase() var content = it.forward.summary val descriptions = it.forward.description @@ -552,7 +552,7 @@ suspend fun List.toRichText(contact: Contact): Result