mirror of
https://github.com/whitechi73/OpenShamrock.git
synced 2024-08-14 05:12:17 +00:00
Compare commits
2 Commits
a528030cbb
...
cb4268edef
Author | SHA1 | Date | |
---|---|---|---|
cb4268edef | |||
c16f9d543c |
2
kritor
2
kritor
Submodule kritor updated: 505d80b2ee...201e91e732
@ -42,7 +42,7 @@ object AuthInterceptor: ServerInterceptor {
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAllTicket(): List<String> {
|
||||
fun getAllTicket(): List<String> {
|
||||
val result = arrayListOf<String>()
|
||||
val activeTicketName = ActiveTicket.name()
|
||||
var index = 0
|
||||
|
@ -20,6 +20,7 @@ class KritorServer(
|
||||
.intercept(AuthInterceptor)
|
||||
.addService(Authentication)
|
||||
.addService(ContactService)
|
||||
.addService(KritorService)
|
||||
.build()!!
|
||||
|
||||
fun start(block: Boolean = false) {
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
118
xposed/src/main/java/kritor/service/KritorService.kt
Normal file
118
xposed/src/main/java/kritor/service/KritorService.kt
Normal file
@ -0,0 +1,118 @@
|
||||
package kritor.service
|
||||
|
||||
import android.util.Base64
|
||||
import com.tencent.mobileqq.app.QQAppInterface
|
||||
import io.grpc.Status
|
||||
import io.grpc.StatusRuntimeException
|
||||
import io.kritor.core.ClearCacheRequest
|
||||
import io.kritor.core.ClearCacheResponse
|
||||
import io.kritor.core.DownloadFileRequest
|
||||
import io.kritor.core.DownloadFileResponse
|
||||
import io.kritor.core.GetCurrentAccountRequest
|
||||
import io.kritor.core.GetCurrentAccountResponse
|
||||
import io.kritor.core.GetVersionRequest
|
||||
import io.kritor.core.GetVersionResponse
|
||||
import io.kritor.core.KritorServiceGrpcKt
|
||||
import io.kritor.core.SwitchAccountRequest
|
||||
import io.kritor.core.SwitchAccountResponse
|
||||
import io.kritor.core.clearCacheResponse
|
||||
import io.kritor.core.downloadFileResponse
|
||||
import io.kritor.core.getCurrentAccountResponse
|
||||
import io.kritor.core.getVersionResponse
|
||||
import io.kritor.core.switchAccountResponse
|
||||
import moe.fuqiuluo.shamrock.tools.ShamrockVersion
|
||||
import moe.fuqiuluo.shamrock.utils.DownloadUtils
|
||||
import moe.fuqiuluo.shamrock.utils.FileUtils
|
||||
import moe.fuqiuluo.shamrock.utils.MD5
|
||||
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
|
||||
import mqq.app.MobileQQ
|
||||
import qq.service.QQInterfaces
|
||||
import qq.service.QQInterfaces.Companion.app
|
||||
import qq.service.contact.ContactHelper
|
||||
import java.io.File
|
||||
|
||||
object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBase() {
|
||||
@Grpc("KritorService", "GetVersion")
|
||||
override suspend fun getVersion(request: GetVersionRequest): GetVersionResponse {
|
||||
return getVersionResponse {
|
||||
this.version = ShamrockVersion
|
||||
this.appName = "Shamrock"
|
||||
}
|
||||
}
|
||||
|
||||
@Grpc("KritorService", "ClearCache")
|
||||
override suspend fun clearCache(request: ClearCacheRequest): ClearCacheResponse {
|
||||
FileUtils.clearCache()
|
||||
MMKVFetcher.mmkvWithId("audio2silk")
|
||||
.clear()
|
||||
return clearCacheResponse {}
|
||||
}
|
||||
|
||||
@Grpc("KritorService", "GetCurrentAccount")
|
||||
override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse {
|
||||
return getCurrentAccountResponse {
|
||||
this.accountName = if (app is QQAppInterface) app.currentNickname else "unknown"
|
||||
this.accountUid = app.currentUid ?: ""
|
||||
this.accountUin = (app.currentUin ?: "0").toLong()
|
||||
}
|
||||
}
|
||||
|
||||
@Grpc("KritorService", "DownloadFile")
|
||||
override suspend fun downloadFile(request: DownloadFileRequest): DownloadFileResponse {
|
||||
val headerMap = mutableMapOf(
|
||||
"User-Agent" to "Shamrock"
|
||||
)
|
||||
if (request.hasHeaders()) {
|
||||
request.headers.split("[\r\n]").forEach {
|
||||
val pair = it.split("=")
|
||||
if (pair.size >= 2) {
|
||||
val (k, v) = pair
|
||||
headerMap[k] = v
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var tmp = FileUtils.getTmpFile("cache")
|
||||
if (request.hasBase64()) {
|
||||
val bytes = Base64.decode(request.base64, Base64.DEFAULT)
|
||||
tmp.writeBytes(bytes)
|
||||
} else if(request.hasUrl()) {
|
||||
if(!DownloadUtils.download(
|
||||
urlAdr = request.url,
|
||||
dest = tmp,
|
||||
headers = headerMap,
|
||||
threadCount = if (request.hasThreadCnt()) request.threadCnt else 3
|
||||
)) {
|
||||
throw StatusRuntimeException(Status.INTERNAL.withDescription("download failed"))
|
||||
}
|
||||
}
|
||||
tmp = if (!request.hasFileName()) FileUtils.renameByMd5(tmp)
|
||||
else tmp.parentFile!!.resolve(request.fileName).also {
|
||||
tmp.renameTo(it)
|
||||
}
|
||||
if (request.hasRootPath()) {
|
||||
tmp = File(request.rootPath).resolve(tmp.name).also {
|
||||
tmp.renameTo(it)
|
||||
}
|
||||
}
|
||||
|
||||
return downloadFileResponse {
|
||||
this.fileMd5 = MD5.genFileMd5Hex(tmp.absolutePath)
|
||||
this.fileAbsolutePath = tmp.absolutePath
|
||||
}
|
||||
}
|
||||
|
||||
@Grpc("KritorService", "SwitchAccount")
|
||||
override suspend fun switchAccount(request: SwitchAccountRequest): SwitchAccountResponse {
|
||||
val uin = if (request.hasAccountUin()) request.accountUin.toString()
|
||||
else ContactHelper.getUinByUidAsync(request.accountUid)
|
||||
val account = MobileQQ.getMobileQQ().allAccounts.firstOrNull { it.uin == uin }
|
||||
?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("account not found"))
|
||||
runCatching {
|
||||
app.switchAccount(account, null)
|
||||
}.onFailure {
|
||||
throw StatusRuntimeException(Status.INTERNAL.withCause(it).withDescription("failed to switch account"))
|
||||
}
|
||||
return switchAccountResponse { }
|
||||
}
|
||||
}
|
@ -33,7 +33,7 @@ object DownloadUtils {
|
||||
threadCount: Int = MAX_THREAD,
|
||||
headers: Map<String, String> = mapOf()
|
||||
): Boolean {
|
||||
var threadCnt = if(threadCount == 0) MAX_THREAD else threadCount
|
||||
var threadCnt = if(threadCount == 0 || threadCount < 0) MAX_THREAD else threadCount
|
||||
val url = URL(urlAdr)
|
||||
val connection = withContext(Dispatchers.IO) { url.openConnection() } as HttpURLConnection
|
||||
headers.forEach { (k, v) ->
|
||||
|
Reference in New Issue
Block a user