From c16f9d543cefd78b5510829df9ad7cdf880cd99c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=99=BD=E6=B1=A0?= Date: Mon, 11 Mar 2024 12:30:42 +0800 Subject: [PATCH] =?UTF-8?q?`Shamrock`:=20=E5=AE=9E=E7=8E=B0=E8=81=94?= =?UTF-8?q?=E7=B3=BB=E4=BA=BA=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: 白池 --- kritor | 2 +- .../main/java/kritor/auth/AuthInterceptor.kt | 2 +- .../java/kritor/service/Authentication.kt | 17 +++ .../java/kritor/service/ContactService.kt | 119 +++++++++++++++++- .../service/contact/ProfileProtocolConst.kt | 39 ++++++ 5 files changed, 173 insertions(+), 6 deletions(-) create mode 100644 xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt diff --git a/kritor b/kritor index 505d80b..201e91e 160000 --- a/kritor +++ b/kritor @@ -1 +1 @@ -Subproject commit 505d80b2eec8d5f226ed28390fb15f4ca41b4c70 +Subproject commit 201e91e73225ce3f1ec098c06a5cf6a717d913e5 diff --git a/xposed/src/main/java/kritor/auth/AuthInterceptor.kt b/xposed/src/main/java/kritor/auth/AuthInterceptor.kt index 37f4186..b195794 100644 --- a/xposed/src/main/java/kritor/auth/AuthInterceptor.kt +++ b/xposed/src/main/java/kritor/auth/AuthInterceptor.kt @@ -42,7 +42,7 @@ object AuthInterceptor: ServerInterceptor { } } - private fun getAllTicket(): List { + fun getAllTicket(): List { val result = arrayListOf() val activeTicketName = ActiveTicket.name() var index = 0 diff --git a/xposed/src/main/java/kritor/service/Authentication.kt b/xposed/src/main/java/kritor/service/Authentication.kt index a6c7814..d0def44 100644 --- a/xposed/src/main/java/kritor/service/Authentication.kt +++ b/xposed/src/main/java/kritor/service/Authentication.kt @@ -1,10 +1,16 @@ package kritor.service +import io.grpc.Status +import io.grpc.StatusRuntimeException import io.kritor.AuthCode import io.kritor.AuthReq 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 import qq.service.QQInterfaces @@ -46,4 +52,15 @@ object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImplBase() { msg = "Invalid ticket" } } + + @Grpc("Authentication", "GetAuthState") + override suspend fun getAuthState(request: GetAuthStateReq): GetAuthStateRsp { + if (request.account != QQInterfaces.app.account) { + throw StatusRuntimeException(Status.CANCELLED.withDescription("No such account")) + } + + return getAuthStateRsp { + isRequiredAuth = AuthInterceptor.getAllTicket().isNotEmpty() + } + } } \ 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 c237678..e973a65 100644 --- a/xposed/src/main/java/kritor/service/ContactService.kt +++ b/xposed/src/main/java/kritor/service/ContactService.kt @@ -1,18 +1,37 @@ package kritor.service +import android.os.Bundle +import com.tencent.mobileqq.profilecard.api.IProfileCardBlacklistApi +import com.tencent.mobileqq.profilecard.api.IProfileProtocolConst.* +import com.tencent.mobileqq.profilecard.api.IProfileProtocolService +import com.tencent.mobileqq.qroute.QRoute import io.grpc.Status import io.grpc.StatusRuntimeException -import io.kritor.AuthCode -import io.kritor.authRsp import io.kritor.contact.ContactServiceGrpcKt +import io.kritor.contact.GetUidRequest +import io.kritor.contact.GetUidResponse +import io.kritor.contact.GetUinByUidRequest +import io.kritor.contact.GetUinByUidResponse +import io.kritor.contact.IsBlackListUserRequest +import io.kritor.contact.IsBlackListUserResponse import io.kritor.contact.ProfileCard import io.kritor.contact.ProfileCardRequest +import io.kritor.contact.SetProfileCardRequest +import io.kritor.contact.SetProfileCardResponse +import io.kritor.contact.StrangerExt +import io.kritor.contact.StrangerInfo +import io.kritor.contact.StrangerInfoRequest import io.kritor.contact.profileCard -import moe.fuqiuluo.shamrock.config.ActiveTicket -import moe.fuqiuluo.shamrock.config.ShamrockConfig +import io.kritor.contact.strangerInfo +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 object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() { + @Grpc("ContactService", "GetProfileCard") override suspend fun getProfileCard(request: ProfileCardRequest): ProfileCard { val uin = if (request.hasUin()) request.uin else ContactHelper.getUinByUidAsync(request.uid).toLong() @@ -44,4 +63,96 @@ object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() { .withDescription("logic failed") ) } + + @Grpc("ContactService", "GetStrangerInfo") + 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") + ) + }.getOrThrow() + + return strangerInfo { + this.uid = ContactHelper.getUidByUinAsync(userId) + this.uin = (info.uin ?: "0").toLong() + this.name = info.strNick ?: "" + this.level = info.iQQLevel + this.loginDay = info.lLoginDays.toInt() + 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() + } + } + + @Grpc("ContactService", "GetUid") + override suspend fun getUid(request: GetUidRequest): GetUidResponse { + return GetUidResponse.newBuilder().apply { + request.uinList.forEach { + putUid(it, ContactHelper.getUidByUinAsync(it)) + } + }.build() + } + + @Grpc("ContactService", "GetUinByUid") + override suspend fun getUinByUid(request: GetUinByUidRequest): GetUinByUidResponse { + return GetUinByUidResponse.newBuilder().apply { + request.uidList.forEach { + putUin(it, ContactHelper.getUinByUidAsync(it).toLong()) + } + }.build() + } + + @Grpc("ContactService", "SetProfileCard") + override suspend fun setProfileCard(request: SetProfileCardRequest): SetProfileCardResponse { + val bundle = Bundle() + val service = QQInterfaces.app + .getRuntimeService(IProfileProtocolService::class.java, "all") + if (request.hasNickName()) { + bundle.putString(KEY_NICK, request.nickName) + } + if (request.hasCompany()) { + bundle.putString(KEY_COMPANY, request.company) + } + if (request.hasEmail()) { + bundle.putString(KEY_EMAIL, request.email) + } + if (request.hasCollege()) { + bundle.putString(KEY_COLLEGE, request.college) + } + if (request.hasPersonalNote()) { + bundle.putString(KEY_PERSONAL_NOTE, request.personalNote) + } + + if (request.hasBirthday()) { + bundle.putInt(KEY_BIRTHDAY, request.birthday) + } + if (request.hasAge()) { + bundle.putInt(KEY_AGE, request.age) + } + + service.setProfileDetail(bundle) + return super.setProfileCard(request) + } + + @Grpc("ContactService", "IsBlackListUser") + override suspend fun isBlackListUser(request: IsBlackListUserRequest): IsBlackListUserResponse { + val blacklistApi = QRoute.api(IProfileCardBlacklistApi::class.java) + val isBlack = withTimeoutOrNull(5000) { + suspendCancellableCoroutine { continuation -> + blacklistApi.isBlackOrBlackedUin(request.uin.toString()) { + continuation.resume(it) + } + } + } ?: false + return IsBlackListUserResponse.newBuilder().setIsBlackListUser(isBlack).build() + } } \ No newline at end of file diff --git a/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt b/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt new file mode 100644 index 0000000..cf486c0 --- /dev/null +++ b/xposed/src/main/java/qq/service/contact/ProfileProtocolConst.kt @@ -0,0 +1,39 @@ +package qq.service.contact + +const val CMD_GET_PROFILE_DETAIL = "OidbSvc.0x480_9_IMCore" +const val CMD_SET_PROFILE_DETAIL = "OidbSvc.0x4ff_9_IMCore" +const val KET_INTERESTS = "interests" +const val KEY_AGE = "age" +const val KEY_BIRTHDAY = "birthday" +const val KEY_COLLEGE = "college" +const val KEY_COMPANY = "company" +const val KEY_CONSTELLATION = "key_constellation" +const val KEY_EMAIL = "email" +const val KEY_HOMETOWN = "hometown" +const val KEY_HOMETOWN_DESC = "hometown_desc" +const val KEY_LOCATION = "location" +const val KEY_LOCATION_DESC = "location_desc" +const val KEY_LOCATION_NAME = "location_name" +const val KEY_NICK = "nick" +const val KEY_PARSE_PROFILE_LOCATION = "parse_profile_location" +const val KEY_PERSONAL_NOTE = "personalNote" +const val KEY_PROFESSION = "profession" +const val KEY_SEX = "sex" +const val PARAM_ADD_FRIEND_SOURCE = "addFriendSource" +const val PARAM_COME_FROM_TYPE = "comeFromType" +const val PARAM_GET_CONTROL = "getControl" +const val PARAM_IS_FRIEND = "isFriend" +const val PARAM_LOGIN_SIG = "loginSig" +const val PARAM_QZONE_FEED_TIMESTAMP = "qZoneFeedTimeStamp" +const val PARAM_QZONE_SEED = "qZoneSeed" +const val PARAM_REQ_0X5EB = "req0x5ebList" +const val PARAM_REQ_EXTEND = "reqExtendFriend" +const val PARAM_REQ_MEDAL = "reqMedalWall" +const val PARAM_REQ_SERVICES = "reqServiceList" +const val PARAM_REQ_TEMPLATE = "reqTemplate" +const val PARAM_SEARCH_NAME = "searchName" +const val PARAM_SECURE_SIG = "secureSig" +const val PARAM_SELF_UIN = "selfUin" +const val PARAM_TARGET_UIN = "targetUin" +const val PARAM_TROOP_CODE = "troopCode" +const val PARAM_TROOP_UIN = "troopUin" \ No newline at end of file