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 result = arrayListOf<String>()
|
||||||
val activeTicketName = ActiveTicket.name()
|
val activeTicketName = ActiveTicket.name()
|
||||||
var index = 0
|
var index = 0
|
||||||
|
@ -20,6 +20,7 @@ class KritorServer(
|
|||||||
.intercept(AuthInterceptor)
|
.intercept(AuthInterceptor)
|
||||||
.addService(Authentication)
|
.addService(Authentication)
|
||||||
.addService(ContactService)
|
.addService(ContactService)
|
||||||
|
.addService(KritorService)
|
||||||
.build()!!
|
.build()!!
|
||||||
|
|
||||||
fun start(block: Boolean = false) {
|
fun start(block: Boolean = false) {
|
||||||
|
@ -1,10 +1,16 @@
|
|||||||
package kritor.service
|
package kritor.service
|
||||||
|
|
||||||
|
import io.grpc.Status
|
||||||
|
import io.grpc.StatusRuntimeException
|
||||||
import io.kritor.AuthCode
|
import io.kritor.AuthCode
|
||||||
import io.kritor.AuthReq
|
import io.kritor.AuthReq
|
||||||
import io.kritor.AuthRsp
|
import io.kritor.AuthRsp
|
||||||
import io.kritor.AuthenticationGrpcKt
|
import io.kritor.AuthenticationGrpcKt
|
||||||
|
import io.kritor.GetAuthStateReq
|
||||||
|
import io.kritor.GetAuthStateRsp
|
||||||
import io.kritor.authRsp
|
import io.kritor.authRsp
|
||||||
|
import io.kritor.getAuthStateRsp
|
||||||
|
import kritor.auth.AuthInterceptor
|
||||||
import moe.fuqiuluo.shamrock.config.ActiveTicket
|
import moe.fuqiuluo.shamrock.config.ActiveTicket
|
||||||
import moe.fuqiuluo.shamrock.config.ShamrockConfig
|
import moe.fuqiuluo.shamrock.config.ShamrockConfig
|
||||||
import qq.service.QQInterfaces
|
import qq.service.QQInterfaces
|
||||||
@ -46,4 +52,15 @@ object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImplBase() {
|
|||||||
msg = "Invalid ticket"
|
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
|
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.Status
|
||||||
import io.grpc.StatusRuntimeException
|
import io.grpc.StatusRuntimeException
|
||||||
import io.kritor.AuthCode
|
|
||||||
import io.kritor.authRsp
|
|
||||||
import io.kritor.contact.ContactServiceGrpcKt
|
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.ProfileCard
|
||||||
import io.kritor.contact.ProfileCardRequest
|
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 io.kritor.contact.profileCard
|
||||||
import moe.fuqiuluo.shamrock.config.ActiveTicket
|
import io.kritor.contact.strangerInfo
|
||||||
import moe.fuqiuluo.shamrock.config.ShamrockConfig
|
import kotlinx.coroutines.suspendCancellableCoroutine
|
||||||
|
import kotlinx.coroutines.withTimeoutOrNull
|
||||||
|
import qq.service.QQInterfaces
|
||||||
import qq.service.contact.ContactHelper
|
import qq.service.contact.ContactHelper
|
||||||
|
import kotlin.coroutines.resume
|
||||||
|
import kotlin.coroutines.suspendCoroutine
|
||||||
|
|
||||||
object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() {
|
object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() {
|
||||||
|
@Grpc("ContactService", "GetProfileCard")
|
||||||
override suspend fun getProfileCard(request: ProfileCardRequest): ProfileCard {
|
override suspend fun getProfileCard(request: ProfileCardRequest): ProfileCard {
|
||||||
val uin = if (request.hasUin()) request.uin
|
val uin = if (request.hasUin()) request.uin
|
||||||
else ContactHelper.getUinByUidAsync(request.uid).toLong()
|
else ContactHelper.getUinByUidAsync(request.uid).toLong()
|
||||||
@ -44,4 +63,96 @@ object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() {
|
|||||||
.withDescription("logic failed")
|
.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,
|
threadCount: Int = MAX_THREAD,
|
||||||
headers: Map<String, String> = mapOf()
|
headers: Map<String, String> = mapOf()
|
||||||
): Boolean {
|
): 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 url = URL(urlAdr)
|
||||||
val connection = withContext(Dispatchers.IO) { url.openConnection() } as HttpURLConnection
|
val connection = withContext(Dispatchers.IO) { url.openConnection() } as HttpURLConnection
|
||||||
headers.forEach { (k, v) ->
|
headers.forEach { (k, v) ->
|
||||||
|
Reference in New Issue
Block a user