update kritor

This commit is contained in:
Simplxs 2024-03-27 16:20:39 +08:00
parent 4a396b0935
commit c014e85faa
No known key found for this signature in database
GPG Key ID: E23537FF14DD6507
25 changed files with 759 additions and 733 deletions

View File

@ -37,17 +37,18 @@ android {
dependencies { dependencies {
protobuf(files("kritor/protos")) protobuf(files("kritor/protos"))
implementation("com.google.protobuf:protobuf-java:4.26.0")
implementation(kotlinx("coroutines-core", "1.8.0")) implementation(kotlinx("coroutines-core", "1.8.0"))
implementation("com.google.protobuf:protobuf-java:3.25.3")
implementation(grpc("stub", "1.62.2")) implementation(grpc("stub", "1.62.2"))
implementation(grpc("kotlin-stub", "1.4.1"))
implementation(grpc("protobuf", "1.62.2")) implementation(grpc("protobuf", "1.62.2"))
implementation(grpc("kotlin-stub", "1.4.1"))
} }
protobuf { protobuf {
protoc { protoc {
artifact = "com.google.protobuf:protoc:3.25.3" artifact = "com.google.protobuf:protoc:4.26.0"
} }
plugins { plugins {
create("grpc") { create("grpc") {

@ -1 +1 @@
Subproject commit 2b29978a5864492267f4afd2b040b890e2f3c182 Subproject commit c49df3074cf193cdbdc49c3c29fda66a0d0110e8

View File

@ -5,22 +5,9 @@ package moe.fuqiuluo.ksp.impl
import com.google.devtools.ksp.KspExperimental import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getClassDeclarationByName import com.google.devtools.ksp.processing.*
import com.google.devtools.ksp.getJavaClassByName
import com.google.devtools.ksp.getKotlinClassByName
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.KSAnnotated import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.symbol.KSFunctionDeclaration import com.google.devtools.ksp.symbol.KSFunctionDeclaration
import com.google.devtools.ksp.symbol.KSType
import com.google.devtools.ksp.symbol.KSTypeParameter
import com.google.devtools.ksp.symbol.Modifier
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.FileSpec import com.squareup.kotlinpoet.FileSpec
import com.squareup.kotlinpoet.FunSpec import com.squareup.kotlinpoet.FunSpec
import com.squareup.kotlinpoet.KModifier import com.squareup.kotlinpoet.KModifier
@ -67,15 +54,16 @@ class GrpcProcessor(
} }
funcBuilder.addStatement("return EMPTY_BYTE_ARRAY") funcBuilder.addStatement("return EMPTY_BYTE_ARRAY")
fileSpec fileSpec
.addStatement("import io.kritor.*") .addStatement("import io.kritor.authentication.*")
.addStatement("import io.kritor.core.*") .addStatement("import io.kritor.core.*")
.addStatement("import io.kritor.contact.*") .addStatement("import io.kritor.customization.*")
.addStatement("import io.kritor.group.*") .addStatement("import io.kritor.developer.*")
.addStatement("import io.kritor.friend.*")
.addStatement("import io.kritor.file.*") .addStatement("import io.kritor.file.*")
.addStatement("import io.kritor.friend.*")
.addStatement("import io.kritor.group.*")
.addStatement("import io.kritor.guild.*")
.addStatement("import io.kritor.message.*") .addStatement("import io.kritor.message.*")
.addStatement("import io.kritor.web.*") .addStatement("import io.kritor.web.*")
.addStatement("import io.kritor.developer.*")
.addFunction(funcBuilder.build()) .addFunction(funcBuilder.build())
.addImport("moe.fuqiuluo.symbols", "EMPTY_BYTE_ARRAY") .addImport("moe.fuqiuluo.symbols", "EMPTY_BYTE_ARRAY")
runCatching { runCatching {

View File

@ -4,11 +4,12 @@ package kritor.client
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import io.grpc.ManagedChannel import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder import io.grpc.ManagedChannelBuilder
import io.kritor.ReverseServiceGrpcKt import io.kritor.common.Request
import io.kritor.event.* import io.kritor.common.Response
import io.kritor.ReqCode import io.kritor.event.EventServiceGrpcKt
import io.kritor.Request import io.kritor.event.EventStructure
import io.kritor.Response import io.kritor.event.EventType
import io.kritor.reverse.ReverseServiceGrpcKt
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
@ -113,7 +114,7 @@ internal class KritorClient(
val rsp = handleGrpc(request.cmd, request.buf.toByteArray()) val rsp = handleGrpc(request.cmd, request.buf.toByteArray())
senderChannel.emit(Response.newBuilder() senderChannel.emit(Response.newBuilder()
.setCmd(request.cmd) .setCmd(request.cmd)
.setCode(ReqCode.SUCCESS) .setCode(Response.ResponseCode.SUCCESS)
.setMsg("success") .setMsg("success")
.setSeq(request.seq) .setSeq(request.seq)
.setBuf(ByteString.copyFrom(rsp)) .setBuf(ByteString.copyFrom(rsp))
@ -121,7 +122,7 @@ internal class KritorClient(
}.onFailure { }.onFailure {
senderChannel.emit(Response.newBuilder() senderChannel.emit(Response.newBuilder()
.setCmd(request.cmd) .setCmd(request.cmd)
.setCode(ReqCode.INTERNAL) .setCode(Response.ResponseCode.INTERNAL)
.setMsg(it.stackTraceToString()) .setMsg(it.stackTraceToString())
.setSeq(request.seq) .setSeq(request.seq)
.setBuf(ByteString.EMPTY) .setBuf(ByteString.EMPTY)

View File

@ -18,8 +18,7 @@ class KritorServer(
private val server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create()) private val server = Grpc.newServerBuilderForPort(port, InsecureServerCredentials.create())
.executor(Dispatchers.IO.asExecutor()) .executor(Dispatchers.IO.asExecutor())
.intercept(AuthInterceptor) .intercept(AuthInterceptor)
.addService(Authentication) .addService(AuthenticationService)
.addService(ContactService)
.addService(CoreService) .addService(CoreService)
.addService(FriendService) .addService(FriendService)
.addService(GroupService) .addService(GroupService)

View File

@ -2,23 +2,19 @@ package kritor.service
import io.grpc.Status import io.grpc.Status
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import io.kritor.AuthCode import io.kritor.authentication.*
import io.kritor.AuthReq import io.kritor.authentication.AuthenticateResponse.AuthenticateResponseCode
import io.kritor.AuthRsp
import io.kritor.AuthenticationServiceGrpcKt
import io.kritor.GetAuthStateReq
import io.kritor.GetAuthStateRsp
import kritor.auth.AuthInterceptor 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
internal object Authentication: AuthenticationServiceGrpcKt.AuthenticationServiceCoroutineImplBase() { internal object AuthenticationService: AuthenticationServiceGrpcKt.AuthenticationServiceCoroutineImplBase() {
@Grpc("Authentication", "Auth") @Grpc("AuthenticationService", "Authenticate")
override suspend fun auth(request: AuthReq): AuthRsp { override suspend fun authenticate(request: AuthenticateRequest): AuthenticateResponse {
if (QQInterfaces.app.account != request.account) { if (QQInterfaces.app.account != request.account) {
return AuthRsp.newBuilder().apply { return AuthenticateResponse.newBuilder().apply {
code = AuthCode.NO_ACCOUNT code = AuthenticateResponseCode.NO_ACCOUNT
msg = "No such account" msg = "No such account"
}.build() }.build()
} }
@ -29,36 +25,36 @@ internal object Authentication: AuthenticationServiceGrpcKt.AuthenticationServic
val ticket = ShamrockConfig.getProperty(activeTicketName + if (index == 0) "" else ".$index", null) val ticket = ShamrockConfig.getProperty(activeTicketName + if (index == 0) "" else ".$index", null)
if (ticket.isNullOrEmpty()) { if (ticket.isNullOrEmpty()) {
if (index == 0) { if (index == 0) {
return AuthRsp.newBuilder().apply { return AuthenticateResponse.newBuilder().apply {
code = AuthCode.OK code = AuthenticateResponseCode.OK
msg = "OK" msg = "OK"
}.build() }.build()
} else { } else {
break break
} }
} else if (ticket == request.ticket) { } else if (ticket == request.ticket) {
return AuthRsp.newBuilder().apply { return AuthenticateResponse.newBuilder().apply {
code = AuthCode.OK code = AuthenticateResponseCode.OK
msg = "OK" msg = "OK"
}.build() }.build()
} }
index++ index++
} }
return AuthRsp.newBuilder().apply { return AuthenticateResponse.newBuilder().apply {
code = AuthCode.NO_TICKET code = AuthenticateResponseCode.NO_TICKET
msg = "Invalid ticket" msg = "Invalid ticket"
}.build() }.build()
} }
@Grpc("Authentication", "GetAuthState") @Grpc("AuthenticationService", "GetAuthenticationState")
override suspend fun getAuthState(request: GetAuthStateReq): GetAuthStateRsp { override suspend fun getAuthenticationState(request: GetAuthenticationStateRequest): GetAuthenticationStateResponse {
if (request.account != QQInterfaces.app.account) { if (request.account != QQInterfaces.app.account) {
throw StatusRuntimeException(Status.CANCELLED.withDescription("No such account")) throw StatusRuntimeException(Status.CANCELLED.withDescription("No such account"))
} }
return GetAuthStateRsp.newBuilder().apply { return GetAuthenticationStateResponse.newBuilder().apply {
isRequiredAuth = AuthInterceptor.getAllTicket().isNotEmpty() isRequired = AuthInterceptor.getAllTicket().isNotEmpty()
}.build() }.build()
} }
} }

View File

@ -1,187 +0,0 @@
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.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.VoteUserRequest
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
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())
)
}
return VoteUserResponse.newBuilder().build()
}
@Grpc("ContactService", "GetProfileCard")
override suspend fun getProfileCard(request: ProfileCardRequest): ProfileCard {
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")
)
}
val contact = ContactHelper.getProfileCard(uin)
contact.onFailure {
throw StatusRuntimeException(
Status.INTERNAL
.withDescription(it.stackTraceToString())
)
}
contact.onSuccess {
return ProfileCard.newBuilder().apply {
this.uin = it.uin.toLong()
this.uid = if (request.hasAccountUid()) request.accountUid
else ContactHelper.getUidByUinAsync(it.uin.toLong())
this.name = it.strNick ?: ""
this.remark = it.strReMark ?: ""
this.level = it.iQQLevel
this.birthday = it.lBirthday
this.loginDay = it.lLoginDays.toInt()
this.voteCnt = it.lVoteCount.toInt()
this.qid = it.qid ?: ""
this.isSchoolVerified = it.schoolVerifiedFlag
}.build()
}
throw StatusRuntimeException(
Status.INTERNAL
.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.newBuilder().apply {
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().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")
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()
}
}

View File

@ -9,8 +9,6 @@ import moe.fuqiuluo.shamrock.tools.ShamrockVersion
import moe.fuqiuluo.shamrock.utils.DownloadUtils import moe.fuqiuluo.shamrock.utils.DownloadUtils
import moe.fuqiuluo.shamrock.utils.FileUtils import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MD5 import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import mqq.app.MobileQQ import mqq.app.MobileQQ
import qq.service.QQInterfaces.Companion.app import qq.service.QQInterfaces.Companion.app
import qq.service.contact.ContactHelper import qq.service.contact.ContactHelper
@ -25,14 +23,6 @@ internal object CoreService : CoreServiceGrpcKt.CoreServiceCoroutineImplBase() {
}.build() }.build()
} }
@Grpc("CoreService", "ClearCache")
override suspend fun clearCache(request: ClearCacheRequest): ClearCacheResponse {
FileUtils.clearCache()
MMKVFetcher.mmkvWithId("audio2silk")
.clear()
return ClearCacheResponse.newBuilder().build()
}
@Grpc("CoreService", "GetCurrentAccount") @Grpc("CoreService", "GetCurrentAccount")
override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse { override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse {
return GetCurrentAccountResponse.newBuilder().apply { return GetCurrentAccountResponse.newBuilder().apply {
@ -108,15 +98,4 @@ internal object CoreService : CoreServiceGrpcKt.CoreServiceCoroutineImplBase() {
} }
return SwitchAccountResponse.newBuilder().build() return SwitchAccountResponse.newBuilder().build()
} }
@Grpc("CoreService", "GetDeviceBattery")
override suspend fun getDeviceBattery(request: GetDeviceBatteryRequest): GetDeviceBatteryResponse {
return GetDeviceBatteryResponse.newBuilder().apply {
PlatformUtils.getDeviceBattery().let {
this.battery = it.battery
this.scale = it.scale
this.status = it.status
}
}.build()
}
} }

View File

@ -2,9 +2,31 @@ package kritor.service
import com.google.protobuf.ByteString import com.google.protobuf.ByteString
import io.kritor.developer.* import io.kritor.developer.*
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import qq.service.QQInterfaces import qq.service.QQInterfaces
internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCoroutineImplBase() { internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCoroutineImplBase() {
@Grpc("DeveloperService", "ClearCache")
override suspend fun clearCache(request: ClearCacheRequest): ClearCacheResponse {
FileUtils.clearCache()
MMKVFetcher.mmkvWithId("audio2silk")
.clear()
return ClearCacheResponse.newBuilder().build()
}
@Grpc("DeveloperService", "GetDeviceBattery")
override suspend fun getDeviceBattery(request: GetDeviceBatteryRequest): GetDeviceBatteryResponse {
return GetDeviceBatteryResponse.newBuilder().apply {
PlatformUtils.getDeviceBattery().let {
this.battery = it.battery
this.scale = it.scale
this.status = it.status
}
}.build()
}
@Grpc("DeveloperService", "SendPacket") @Grpc("DeveloperService", "SendPacket")
override suspend fun sendPacket(request: SendPacketRequest): SendPacketResponse { override suspend fun sendPacket(request: SendPacketRequest): SendPacketResponse {
return SendPacketResponse.newBuilder().apply { return SendPacketResponse.newBuilder().apply {

View File

@ -1,23 +1,33 @@
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.friend.* import io.kritor.friend.*
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import qq.service.QQInterfaces
import qq.service.contact.ContactHelper import qq.service.contact.ContactHelper
import qq.service.friend.FriendHelper import qq.service.friend.FriendHelper
import kotlin.coroutines.resume
internal object FriendService : FriendServiceGrpcKt.FriendServiceCoroutineImplBase() { internal object FriendService : FriendServiceGrpcKt.FriendServiceCoroutineImplBase() {
@Grpc("FriendService", "GetFriendList") @Grpc("FriendService", "GetFriendList")
override suspend fun getFriendList(request: GetFriendListRequest): GetFriendListResponse { override suspend fun getFriendList(request: GetFriendListRequest): GetFriendListResponse {
val friendList = FriendHelper.getFriendList(if (request.hasRefresh()) request.refresh else false).onFailure { val friendList = FriendHelper.getFriendList(if (request.hasRefresh()) request.refresh else false).onFailure {
throw StatusRuntimeException(Status.INTERNAL throw StatusRuntimeException(
Status.INTERNAL
.withDescription(it.stackTraceToString()) .withDescription(it.stackTraceToString())
) )
}.getOrThrow() }.getOrThrow()
return GetFriendListResponse.newBuilder().apply { return GetFriendListResponse.newBuilder().apply {
friendList.forEach { friendList.forEach {
this.addFriendList(FriendData.newBuilder().apply { this.addFriendsInfo(FriendInfo.newBuilder().apply {
uin = it.uin.toLong() uin = it.uin.toLong()
uid = ContactHelper.getUidByUinAsync(uin) uid = ContactHelper.getUidByUinAsync(uin)
qid = "" qid = ""
@ -27,10 +37,208 @@ internal object FriendService: FriendServiceGrpcKt.FriendServiceCoroutineImplBas
level = 0 level = 0
gender = it.gender.toInt() gender = it.gender.toInt()
groupId = it.groupid groupId = it.groupid
ext = FriendExt.newBuilder().build().toByteString()
ext = ExtInfo.newBuilder().build()
}) })
} }
}.build() }.build()
} }
@Grpc("FriendService", "GetFriendProfileCard")
override suspend fun getFriendProfileCard(request: GetFriendProfileCardRequest): GetFriendProfileCardResponse {
return GetFriendProfileCardResponse.newBuilder().apply {
request.targetUinsList.forEach {
ContactHelper.getProfileCard(it).getOrThrow().let { info ->
addFriendsProfileCard(ProfileCard.newBuilder().apply {
this.uin = info.uin.toLong()
this.uid = ContactHelper.getUidByUinAsync(info.uin.toLong())
this.nick = info.strNick
this.remark = info.strReMark
this.level = info.iQQLevel
this.birthday = info.lBirthday
this.loginDay = info.lLoginDays.toInt()
this.voteCnt = info.lVoteCount.toInt()
this.qid = info.qid ?: ""
this.isSchoolVerified = info.schoolVerifiedFlag
this.ext = ExtInfo.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()
}.build())
}
}
request.targetUidsList.forEach {
ContactHelper.getProfileCard(ContactHelper.getUinByUidAsync(it).toLong()).getOrThrow().let { info ->
addFriendsProfileCard(ProfileCard.newBuilder().apply {
this.uin = info.uin.toLong()
this.uid = it
this.nick = info.strNick
this.remark = info.strReMark
this.level = info.iQQLevel
this.birthday = info.lBirthday
this.loginDay = info.lLoginDays.toInt()
this.voteCnt = info.lVoteCount.toInt()
this.qid = info.qid ?: ""
this.isSchoolVerified = info.schoolVerifiedFlag
this.ext = ExtInfo.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()
}.build())
}
}
}.build()
}
@Grpc("FriendService", "GetStrangerProfileCard")
override suspend fun getStrangerProfileCard(request: GetStrangerProfileCardRequest): GetStrangerProfileCardResponse {
return GetStrangerProfileCardResponse.newBuilder().apply {
request.targetUinsList.forEach {
ContactHelper.refreshAndGetProfileCard(it).getOrThrow().let { info ->
addStrangersProfileCard(ProfileCard.newBuilder().apply {
this.uin = info.uin.toLong()
this.uid = ContactHelper.getUidByUinAsync(info.uin.toLong())
this.nick = info.strNick
this.level = info.iQQLevel
this.birthday = info.lBirthday
this.loginDay = info.lLoginDays.toInt()
this.voteCnt = info.lVoteCount.toInt()
this.qid = info.qid ?: ""
this.isSchoolVerified = info.schoolVerifiedFlag
this.ext = ExtInfo.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()
}.build())
}
}
request.targetUidsList.forEach {
ContactHelper.refreshAndGetProfileCard(ContactHelper.getUinByUidAsync(it).toLong()).getOrThrow()
.let { info ->
addStrangersProfileCard(ProfileCard.newBuilder().apply {
this.uin = info.uin.toLong()
this.uid = it
this.nick = info.strNick
this.level = info.iQQLevel
this.birthday = info.lBirthday
this.loginDay = info.lLoginDays.toInt()
this.voteCnt = info.lVoteCount.toInt()
this.qid = info.qid ?: ""
this.isSchoolVerified = info.schoolVerifiedFlag
this.ext = ExtInfo.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()
}.build())
}
}
}.build()
}
@Grpc("FriendService", "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(IProfileProtocolConst.KEY_NICK, request.nickName)
}
if (request.hasCompany()) {
bundle.putString(IProfileProtocolConst.KEY_COMPANY, request.company)
}
if (request.hasEmail()) {
bundle.putString(IProfileProtocolConst.KEY_EMAIL, request.email)
}
if (request.hasCollege()) {
bundle.putString(IProfileProtocolConst.KEY_COLLEGE, request.college)
}
if (request.hasPersonalNote()) {
bundle.putString(IProfileProtocolConst.KEY_PERSONAL_NOTE, request.personalNote)
}
if (request.hasBirthday()) {
bundle.putInt(IProfileProtocolConst.KEY_BIRTHDAY, request.birthday)
}
if (request.hasAge()) {
bundle.putInt(IProfileProtocolConst.KEY_AGE, request.age)
}
service.setProfileDetail(bundle)
return super.setProfileCard(request)
}
@Grpc("FriendService", "IsBlackListUser")
override suspend fun isBlackListUser(request: IsBlackListUserRequest): IsBlackListUserResponse {
val uin = when (request.targetCase!!) {
IsBlackListUserRequest.TargetCase.TARGET_UIN -> request.targetUin.toString()
IsBlackListUserRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid)
IsBlackListUserRequest.TargetCase.TARGET_NOT_SET -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("account not set")
)
}
val blacklistApi = QRoute.api(IProfileCardBlacklistApi::class.java)
val isBlack = withTimeoutOrNull(5000) {
suspendCancellableCoroutine { continuation ->
blacklistApi.isBlackOrBlackedUin(uin) {
continuation.resume(it)
}
}
} ?: false
return IsBlackListUserResponse.newBuilder().setIsBlackListUser(isBlack).build()
}
@Grpc("FriendService", "VoteUser")
override suspend fun voteUser(request: VoteUserRequest): VoteUserResponse {
ContactHelper.voteUser(
when (request.targetCase!!) {
VoteUserRequest.TargetCase.TARGET_UIN -> request.targetUin
VoteUserRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
VoteUserRequest.TargetCase.TARGET_NOT_SET -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("account not set")
)
}, request.voteCount
).onFailure {
throw StatusRuntimeException(
Status.INTERNAL
.withDescription(it.stackTraceToString())
)
}
return VoteUserResponse.newBuilder().build()
}
@Grpc("FriendService", "GetUidByUin")
override suspend fun getUidByUin(request: GetUidRequest): GetUidResponse {
return GetUidResponse.newBuilder().apply {
request.targetUinsList.forEach {
putUidMap(it, ContactHelper.getUidByUinAsync(it))
}
}.build()
}
@Grpc("FriendService", "GetUinByUid")
override suspend fun getUinByUid(request: GetUinByUidRequest): GetUinByUidResponse {
return GetUinByUidResponse.newBuilder().apply {
request.targetUidsList.forEach {
putUinMap(it, ContactHelper.getUinByUidAsync(it).toLong())
}
}.build()
}
} }

View File

@ -49,13 +49,15 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
@Grpc("GroupFileService", "DeleteFolder") @Grpc("GroupFileService", "DeleteFolder")
override suspend fun deleteFolder(request: DeleteFolderRequest): DeleteFolderResponse { override suspend fun deleteFolder(request: DeleteFolderRequest): DeleteFolderResponse {
val fromServiceMsg = QQInterfaces.sendOidbAW("OidbSvc.0x6d7_1", 1751, 1, Oidb0x6d7ReqBody( val fromServiceMsg = QQInterfaces.sendOidbAW(
"OidbSvc.0x6d7_1", 1751, 1, Oidb0x6d7ReqBody(
deleteFolder = DeleteFolderReq( deleteFolder = DeleteFolderReq(
groupCode = request.groupId.toULong(), groupCode = request.groupId.toULong(),
appId = 3u, appId = 3u,
folderId = request.folderId folderId = request.folderId
) )
).toByteArray()) ?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to send oidb request")) ).toByteArray()
) ?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to send oidb request"))
if (fromServiceMsg.wupBuffer == null) { if (fromServiceMsg.wupBuffer == null) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed")) throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed"))
} }
@ -97,14 +99,16 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
@Grpc("GroupFileService", "RenameFolder") @Grpc("GroupFileService", "RenameFolder")
override suspend fun renameFolder(request: RenameFolderRequest): RenameFolderResponse { override suspend fun renameFolder(request: RenameFolderRequest): RenameFolderResponse {
val fromServiceMsg = QQInterfaces.sendOidbAW("OidbSvc.0x6d7_3", 1751, 3, Oidb0x6d7ReqBody( val fromServiceMsg = QQInterfaces.sendOidbAW(
"OidbSvc.0x6d7_3", 1751, 3, Oidb0x6d7ReqBody(
renameFolder = RenameFolderReq( renameFolder = RenameFolderReq(
groupCode = request.groupId.toULong(), groupCode = request.groupId.toULong(),
appId = 3u, appId = 3u,
folderId = request.folderId, folderId = request.folderId,
folderName = request.name folderName = request.name
) )
).toByteArray()) ?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to send oidb request")) ).toByteArray()
) ?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to send oidb request"))
if (fromServiceMsg.wupBuffer == null) { if (fromServiceMsg.wupBuffer == null) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed")) throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed"))
} }
@ -122,17 +126,11 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
return getGroupFileSystemInfo(request.groupId) return getGroupFileSystemInfo(request.groupId)
} }
@Grpc("GroupFileService", "GetRootFiles") @Grpc("GroupFileService", "GetFileList")
override suspend fun getRootFiles(request: GetRootFilesRequest): GetRootFilesResponse { override suspend fun getFileList(request: GetFileListRequest): GetFileListResponse {
return GetRootFilesResponse.newBuilder().apply { return if (request.hasFolderId())
val response = GroupFileHelper.getGroupFiles(request.groupId) GroupFileHelper.getGroupFiles(request.groupId, request.folderId)
this.addAllFiles(response.filesList) else
this.addAllFolders(response.foldersList) GroupFileHelper.getGroupFiles(request.groupId)
}.build()
}
@Grpc("GroupFileService", "GetFiles")
override suspend fun getFiles(request: GetFilesRequest): GetFilesResponse {
return GroupFileHelper.getGroupFiles(request.groupId, request.folderId)
} }
} }

View File

@ -212,7 +212,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
}.getOrThrow() }.getOrThrow()
return GetGroupListResponse.newBuilder().apply { return GetGroupListResponse.newBuilder().apply {
groupList.forEach { groupInfo -> groupList.forEach { groupInfo ->
this.addGroupInfo(GroupInfo.newBuilder().apply { this.addGroupsInfo(GroupInfo.newBuilder().apply {
groupId = groupInfo.troopcode.toLong() groupId = groupInfo.troopcode.toLong()
groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark } groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }
.ifNullOrEmpty { groupInfo.newTroopName } ?: "" .ifNullOrEmpty { groupInfo.newTroopName } ?: ""
@ -231,8 +231,8 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
override suspend fun getGroupMemberInfo(request: GetGroupMemberInfoRequest): GetGroupMemberInfoResponse { override suspend fun getGroupMemberInfo(request: GetGroupMemberInfoRequest): GetGroupMemberInfoResponse {
val memberInfo = GroupHelper.getTroopMemberInfoByUin( val memberInfo = GroupHelper.getTroopMemberInfoByUin(
request.groupId.toString(), when (request.targetCase!!) { request.groupId.toString(), when (request.targetCase!!) {
GetGroupMemberInfoRequest.TargetCase.UIN -> request.uin GetGroupMemberInfoRequest.TargetCase.TARGET_UID -> request.targetUin
GetGroupMemberInfoRequest.TargetCase.UID -> ContactHelper.getUinByUidAsync(request.uid).toLong() GetGroupMemberInfoRequest.TargetCase.TARGET_UIN -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException( else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT Status.INVALID_ARGUMENT
.withDescription("target not set") .withDescription("target not set")
@ -246,8 +246,8 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
return GetGroupMemberInfoResponse.newBuilder().apply { return GetGroupMemberInfoResponse.newBuilder().apply {
groupMemberInfo = GroupMemberInfo.newBuilder().apply { groupMemberInfo = GroupMemberInfo.newBuilder().apply {
uid = uid =
if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.UID) request.uid else ContactHelper.getUidByUinAsync( if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.TARGET_UID) request.targetUid else ContactHelper.getUidByUinAsync(
request.uin request.targetUin
) )
uin = memberInfo.memberuin?.toLong() ?: 0 uin = memberInfo.memberuin?.toLong() ?: 0
nick = memberInfo.troopnick nick = memberInfo.troopnick
@ -264,7 +264,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
shutUpTimestamp = memberInfo.gagTimeStamp shutUpTimestamp = memberInfo.gagTimeStamp
distance = memberInfo.distance distance = memberInfo.distance
addAllHonor((memberInfo.honorList ?: "") addAllHonors((memberInfo.honorList ?: "")
.split("|") .split("|")
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.map { it.toInt() }) .map { it.toInt() })
@ -286,7 +286,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
}.getOrThrow() }.getOrThrow()
return GetGroupMemberListResponse.newBuilder().apply { return GetGroupMemberListResponse.newBuilder().apply {
memberList.forEach { memberInfo -> memberList.forEach { memberInfo ->
this.addGroupMemberInfo(GroupMemberInfo.newBuilder().apply { this.addGroupMembersInfo(GroupMemberInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(memberInfo.memberuin?.toLong() ?: 0) uid = ContactHelper.getUidByUinAsync(memberInfo.memberuin?.toLong() ?: 0)
uin = memberInfo.memberuin?.toLong() ?: 0 uin = memberInfo.memberuin?.toLong() ?: 0
nick = memberInfo.troopnick nick = memberInfo.troopnick
@ -303,7 +303,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
shutUpTimestamp = memberInfo.gagTimeStamp shutUpTimestamp = memberInfo.gagTimeStamp
distance = memberInfo.distance distance = memberInfo.distance
addAllHonor((memberInfo.honorList ?: "") addAllHonors((memberInfo.honorList ?: "")
.split("|") .split("|")
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.map { it.toInt() }) .map { it.toInt() })
@ -323,7 +323,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
}.getOrThrow() }.getOrThrow()
return GetProhibitedUserListResponse.newBuilder().apply { return GetProhibitedUserListResponse.newBuilder().apply {
prohibitedList.forEach { prohibitedList.forEach {
this.addProhibitedUserInfo(ProhibitedUserInfo.newBuilder().apply { this.addProhibitedUsersInfo(ProhibitedUserInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(it.memberUin) uid = ContactHelper.getUidByUinAsync(it.memberUin)
uin = it.memberUin uin = it.memberUin
prohibitedTime = it.shutuptimestap prohibitedTime = it.shutuptimestap
@ -380,7 +380,7 @@ internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase(
.map { it.toInt() }.forEach { .map { it.toInt() }.forEach {
val honor = decodeHonor(member.memberuin.toLong(), it, member.mHonorRichFlag) val honor = decodeHonor(member.memberuin.toLong(), it, member.mHonorRichFlag)
if (honor != null) { if (honor != null) {
addGroupHonorInfo(GroupHonorInfo.newBuilder().apply { addGroupHonorsInfo(GroupHonorInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(member.memberuin.toLong()) uid = ContactHelper.getUidByUinAsync(member.memberuin.toLong())
uin = member.memberuin.toLong() uin = member.memberuin.toLong()
nick = member.troopnick nick = member.troopnick

View File

@ -7,9 +7,8 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.grpc.Status import io.grpc.Status
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import io.kritor.event.MessageEvent import io.kritor.common.*
import io.kritor.message.* import io.kritor.message.*
import io.kritor.message.EssenceMessage
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
@ -55,7 +54,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
uniseq uniseq
).onFailure { ).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it)) throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow() }.getOrThrow().toString()
}.build() }.build()
} }
@ -89,8 +88,8 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
return SendMessageByResIdResponse.newBuilder().build() return SendMessageByResIdResponse.newBuilder().build()
} }
@Grpc("MessageService", "ClearMessages") @Grpc("MessageService", "SetMessageReaded")
override suspend fun clearMessages(request: ClearMessagesRequest): ClearMessagesResponse { override suspend fun setMessageReaded(request: SetMessageReadRequest): SetMessageReadResponse {
val contact = request.contact val contact = request.contact
val kernelService = NTServiceFetcher.kernelService val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession val sessionService = kernelService.wrapperSession
@ -105,7 +104,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene")) Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
} }
service.clearMsgRecords(Contact(chatType, contact.peer, contact.subPeer), null) service.clearMsgRecords(Contact(chatType, contact.peer, contact.subPeer), null)
return ClearMessagesResponse.newBuilder().build() return SetMessageReadResponse.newBuilder().build()
} }
@Grpc("MessageService", "RecallMessage") @Grpc("MessageService", "RecallMessage")
@ -126,7 +125,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
val kernelService = NTServiceFetcher.kernelService val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession val sessionService = kernelService.wrapperSession
val service = sessionService.msgService val service = sessionService.msgService
service.recallMsg(contact, arrayListOf(request.messageId)) { code, msg -> service.recallMsg(contact, arrayListOf(request.messageId.toLong())) { code, msg ->
if (code != 0) { if (code != 0) {
LogCenter.log("消息撤回失败: $code:$msg", Level.WARN) LogCenter.log("消息撤回失败: $code:$msg", Level.WARN)
} }
@ -153,7 +152,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
val msg: MsgRecord = withTimeoutOrNull(5000) { val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(request.messageId)) { code, _, msgRecords -> service.getMsgsByMsgId(contact, arrayListOf(request.messageId.toLong())) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) { if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first()) continuation.resume(msgRecords.first())
} else { } else {
@ -167,13 +166,13 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
return GetMessageResponse.newBuilder().apply { return GetMessageResponse.newBuilder().apply {
this.message = MessageEvent.newBuilder().apply { this.message = PushMessageBody.newBuilder().apply {
this.messageId = msg.msgId this.messageId = msg.msgId.toString()
this.contact = request.contact this.contact = request.contact
this.sender = Sender.newBuilder().apply { this.sender = Sender.newBuilder().apply {
this.uid = msg.senderUid ?: ""
this.uin = msg.senderUin this.uin = msg.senderUin
this.nick = msg.sendNickName ?: "" this.nick = msg.sendNickName ?: ""
this.uid = msg.senderUid ?: ""
}.build() }.build()
this.messageSeq = msg.msgSeq this.messageSeq = msg.msgSeq
this.addAllElements(msg.elements.toKritorReqMessages(contact)) this.addAllElements(msg.elements.toKritorReqMessages(contact))
@ -213,8 +212,8 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) } ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
return GetMessageBySeqResponse.newBuilder().apply { return GetMessageBySeqResponse.newBuilder().apply {
this.message = MessageEvent.newBuilder().apply { this.message = PushMessageBody.newBuilder().apply {
this.messageId = msg.msgId this.messageId = msg.msgId.toString()
this.contact = request.contact this.contact = request.contact
this.sender = Sender.newBuilder().apply { this.sender = Sender.newBuilder().apply {
this.uin = msg.senderUin this.uin = msg.senderUin
@ -245,7 +244,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
val msgs: List<MsgRecord> = withTimeoutOrNull(5000) { val msgs: List<MsgRecord> = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
service.getMsgs(contact, request.startMessageId, request.count, true) { code, _, msgRecords -> service.getMsgs(contact, request.startMessageId.toLong(), request.count, true) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) { if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords) continuation.resume(msgRecords)
} else { } else {
@ -260,8 +259,8 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
return GetHistoryMessageResponse.newBuilder().apply { return GetHistoryMessageResponse.newBuilder().apply {
msgs.forEach { msgs.forEach {
addMessages(MessageEvent.newBuilder().apply { addMessages(PushMessageBody.newBuilder().apply {
this.messageId = it.msgId this.messageId = it.msgId.toString()
this.contact = request.contact this.contact = request.contact
this.sender = Sender.newBuilder().apply { this.sender = Sender.newBuilder().apply {
this.uin = it.senderUin this.uin = it.senderUin
@ -292,9 +291,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
} }
val forwardMessage = ForwardMessageHelper.uploadMultiMsg( val forwardMessage = ForwardMessageHelper.uploadMultiMsg(
contact.chatType, contact,
contact.longPeer().toString(),
contact.guildId,
request.messagesList request.messagesList
).onFailure { ).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it)) throw StatusRuntimeException(Status.INTERNAL.withCause(it))
@ -312,11 +309,11 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
MessageHelper.getForwardMsg(request.resId).onFailure { MessageHelper.getForwardMsg(request.resId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it)) throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow().map { detail -> }.getOrThrow().map { detail ->
MessageEvent.newBuilder().apply { PushMessageBody.newBuilder().apply {
this.time = detail.time this.time = detail.time
this.messageId = detail.qqMsgId this.messageId = detail.qqMsgId.toString()
this.messageSeq = detail.msgSeq this.messageSeq = detail.msgSeq
this.contact = io.kritor.message.Contact.newBuilder().apply { this.contact = io.kritor.common.Contact.newBuilder().apply {
this.scene = when (detail.msgType) { this.scene = when (detail.msgType) {
MsgConstant.KCHATTYPEC2C -> Scene.FRIEND MsgConstant.KCHATTYPEC2C -> Scene.FRIEND
MsgConstant.KCHATTYPEGROUP -> Scene.GROUP MsgConstant.KCHATTYPEGROUP -> Scene.GROUP
@ -347,13 +344,13 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
}.build() }.build()
} }
@Grpc("MessageService", "DeleteEssenceMsg") @Grpc("MessageService", "DeleteEssenceMessage")
override suspend fun deleteEssenceMsg(request: DeleteEssenceMsgRequest): DeleteEssenceMsgResponse { override suspend fun deleteEssenceMessage(request: DeleteEssenceMessageRequest): DeleteEssenceMessageResponse {
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString()) val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString())
val msg: MsgRecord = withTimeoutOrNull(5000) { val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(request.messageId)) { code, _, msgRecords -> service.getMsgsByMsgId(contact, arrayListOf(request.messageId.toLong())) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) { if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first()) continuation.resume(msgRecords.first())
} else { } else {
@ -367,17 +364,17 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found")) } ?: 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")) throw StatusRuntimeException(Status.NOT_FOUND.withDescription("delete essence message failed"))
return DeleteEssenceMsgResponse.newBuilder().build() return DeleteEssenceMessageResponse.newBuilder().build()
} }
@Grpc("MessageService", "GetEssenceMessages") @Grpc("MessageService", "GetEssenceMessageList")
override suspend fun getEssenceMessages(request: GetEssenceMessagesRequest): GetEssenceMessagesResponse { override suspend fun getEssenceMessageList(request: GetEssenceMessageListRequest): GetEssenceMessageListResponse {
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString()) val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString())
return GetEssenceMessagesResponse.newBuilder().apply { return GetEssenceMessageListResponse.newBuilder().apply {
MessageHelper.getEssenceMessageList(request.groupId, request.page, request.pageSize).onFailure { MessageHelper.getEssenceMessageList(request.groupId, request.page, request.pageSize).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it)) throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow().forEach { }.getOrThrow().forEach {
addEssenceMessage(EssenceMessage.newBuilder().apply { addMessages(EssenceMessageBody.newBuilder().apply {
withTimeoutOrNull(5000) { withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
@ -393,10 +390,10 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
} }
} }
}?.let { }?.let {
this.messageId = it.msgId this.messageId = it.msgId.toString()
} }
this.messageSeq = it.messageSeq this.messageSeq = it.messageSeq
this.msgTime = it.senderTime.toInt() this.messageTime = it.senderTime.toInt()
this.senderNick = it.senderNick this.senderNick = it.senderNick
this.senderUin = it.senderId this.senderUin = it.senderId
this.operationTime = it.operatorTime.toInt() this.operationTime = it.operatorTime.toInt()
@ -414,7 +411,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
val msg: MsgRecord = withTimeoutOrNull(5000) { val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(request.messageId)) { code, _, msgRecords -> service.getMsgsByMsgId(contact, arrayListOf(request.messageId.toLong())) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) { if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first()) continuation.resume(msgRecords.first())
} else { } else {
@ -432,8 +429,8 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
return SetEssenceMessageResponse.newBuilder().build() return SetEssenceMessageResponse.newBuilder().build()
} }
@Grpc("MessageService", "SetMessageCommentEmoji") @Grpc("MessageService", "ReactMessageWithEmoji")
override suspend fun setMessageCommentEmoji(request: SetMessageCommentEmojiRequest): SetMessageCommentEmojiResponse { override suspend fun reactMessageWithEmoji(request: ReactMessageWithEmojiRequest): ReactMessageWithEmojiResponse {
val contact = request.contact.let { val contact = request.contact.let {
MessageHelper.generateContact( MessageHelper.generateContact(
when (it.scene!!) { when (it.scene!!) {
@ -450,7 +447,7 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
val msg: MsgRecord = withTimeoutOrNull(5000) { val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(request.messageId)) { code, _, msgRecords -> service.getMsgsByMsgId(contact, arrayListOf(request.messageId.toLong())) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) { if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first()) continuation.resume(msgRecords.first())
} else { } else {
@ -468,6 +465,6 @@ internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImp
request.faceId.toString(), request.faceId.toString(),
request.isComment request.isComment
) )
return SetMessageCommentEmojiResponse.newBuilder().build() return ReactMessageWithEmojiResponse.newBuilder().build()
} }
} }

View File

@ -11,9 +11,9 @@ internal object QsignService: QsignServiceGrpcKt.QsignServiceCoroutineImplBase()
override suspend fun sign(request: SignRequest): SignResponse { override suspend fun sign(request: SignRequest): SignResponse {
return SignResponse.newBuilder().apply { return SignResponse.newBuilder().apply {
val result = FEKit.getInstance().getSign(request.command, request.buffer.toByteArray(), request.seq, request.uin) val result = FEKit.getInstance().getSign(request.command, request.buffer.toByteArray(), request.seq, request.uin)
this.sign = ByteString.copyFrom(result.sign) this.secSig = ByteString.copyFrom(result.sign)
this.token = ByteString.copyFrom(result.token) this.secDeviceToken = ByteString.copyFrom(result.token)
this.extra = ByteString.copyFrom(result.extra) this.secExtra = ByteString.copyFrom(result.extra)
}.build() }.build()
} }

View File

@ -5,8 +5,9 @@ package moe.fuqiuluo.shamrock.internals
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import io.kritor.event.* import io.kritor.event.*
import io.kritor.message.Contact import io.kritor.common.PushMessageBody
import io.kritor.message.Sender import io.kritor.common.Contact
import io.kritor.common.Sender
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.FlowCollector import kotlinx.coroutines.flow.FlowCollector
@ -17,7 +18,7 @@ import qq.service.msg.toKritorEventMessages
internal object GlobalEventTransmitter : QQInterfaces() { internal object GlobalEventTransmitter : QQInterfaces() {
private val MessageEventFlow by lazy { private val MessageEventFlow by lazy {
MutableSharedFlow<Pair<MsgRecord, MessageEvent>>() MutableSharedFlow<Pair<MsgRecord, PushMessageBody>>()
} }
private val noticeEventFlow by lazy { private val noticeEventFlow by lazy {
MutableSharedFlow<NoticeEvent>() MutableSharedFlow<NoticeEvent>()
@ -30,7 +31,7 @@ internal object GlobalEventTransmitter : QQInterfaces() {
private suspend fun pushRequest(requestEvent: RequestsEvent) = requestEventFlow.emit(requestEvent) private suspend fun pushRequest(requestEvent: RequestsEvent) = requestEventFlow.emit(requestEvent)
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = private suspend fun transMessageEvent(record: MsgRecord, message: PushMessageBody) =
MessageEventFlow.emit(record to message) MessageEventFlow.emit(record to message)
object MessageTransmitter { object MessageTransmitter {
@ -38,9 +39,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
record: MsgRecord, record: MsgRecord,
elements: ArrayList<MsgElement>, elements: ArrayList<MsgElement>,
): Boolean { ): Boolean {
transMessageEvent(record, MessageEvent.newBuilder().apply { transMessageEvent(record, PushMessageBody.newBuilder().apply {
this.time = record.msgTime.toInt() this.time = record.msgTime.toInt()
this.messageId = record.msgId this.messageId = record.msgId.toString()
this.messageSeq = record.msgSeq this.messageSeq = record.msgSeq
this.contact = Contact.newBuilder().apply { this.contact = Contact.newBuilder().apply {
this.scene = scene this.scene = scene
@ -61,9 +62,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
record: MsgRecord, record: MsgRecord,
elements: ArrayList<MsgElement>, elements: ArrayList<MsgElement>,
): Boolean { ): Boolean {
transMessageEvent(record, MessageEvent.newBuilder().apply { transMessageEvent(record, PushMessageBody.newBuilder().apply {
this.time = record.msgTime.toInt() this.time = record.msgTime.toInt()
this.messageId = record.msgId this.messageId = record.msgId.toString()
this.messageSeq = record.msgSeq this.messageSeq = record.msgSeq
this.contact = Contact.newBuilder().apply { this.contact = Contact.newBuilder().apply {
this.scene = scene this.scene = scene
@ -86,9 +87,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupCode: Long, groupCode: Long,
fromNick: String, fromNick: String,
): Boolean { ): Boolean {
transMessageEvent(record, MessageEvent.newBuilder().apply { transMessageEvent(record, PushMessageBody.newBuilder().apply {
this.time = record.msgTime.toInt() this.time = record.msgTime.toInt()
this.messageId = record.msgId this.messageId = record.msgId.toString()
this.messageSeq = record.msgSeq this.messageSeq = record.msgSeq
this.contact = Contact.newBuilder().apply { this.contact = Contact.newBuilder().apply {
this.scene = scene this.scene = scene
@ -109,9 +110,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
record: MsgRecord, record: MsgRecord,
elements: ArrayList<MsgElement>, elements: ArrayList<MsgElement>,
): Boolean { ): Boolean {
transMessageEvent(record, MessageEvent.newBuilder().apply { transMessageEvent(record, PushMessageBody.newBuilder().apply {
this.time = record.msgTime.toInt() this.time = record.msgTime.toInt()
this.messageId = record.msgId this.messageId = record.msgId.toString()
this.messageSeq = record.msgSeq this.messageSeq = record.msgSeq
this.contact = Contact.newBuilder().apply { this.contact = Contact.newBuilder().apply {
this.scene = scene this.scene = scene
@ -147,12 +148,12 @@ internal object GlobalEventTransmitter : QQInterfaces() {
url: String url: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_FILE_COME this.type = NoticeEvent.NoticeType.FRIEND_FILE_COME
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.friendFileCome = FriendFileComeNotice.newBuilder().apply { this.friendFileUploaded = FriendFileUploadedNotice.newBuilder().apply {
this.fileId = fileId this.fileId = fileId
this.fileName = fileName this.fileName = fileName
this.operator = userId this.operatorUin = userId
this.fileSize = fileSize this.fileSize = fileSize
this.expireTime = expireTime.toInt() this.expireTime = expireTime.toInt()
this.fileSubId = fileSubId this.fileSubId = fileSubId
@ -176,11 +177,11 @@ internal object GlobalEventTransmitter : QQInterfaces() {
url: String url: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_FILE_COME this.type = NoticeEvent.NoticeType.GROUP_FILE_COME
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.groupFileCome = GroupFileComeNotice.newBuilder().apply { this.groupFileUploaded = GroupFileUploadedNotice.newBuilder().apply {
this.groupId = groupId this.groupId = groupId
this.operator = userId this.operatorUin = userId
this.fileId = uuid this.fileId = uuid
this.fileName = fileName this.fileName = fileName
this.fileSize = fileSize this.fileSize = fileSize
@ -204,9 +205,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupCode: Long groupCode: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_SIGN this.type = NoticeEvent.NoticeType.GROUP_SIGN
this.time = time.toInt() this.time = time.toInt()
this.groupSign = GroupSignNotice.newBuilder().apply { this.groupSignIn = GroupSignInNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
this.targetUin = target this.targetUin = target
this.action = action ?: "" this.action = action ?: ""
@ -227,12 +228,13 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupCode: Long groupCode: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_POKE this.type = NoticeEvent.NoticeType.GROUP_POKE
this.time = time.toInt() this.time = time.toInt()
this.groupPoke = GroupPokeNotice.newBuilder().apply { this.groupPoke = GroupPokeNotice.newBuilder().apply {
this.groupId = groupCode
this.action = action ?: "" this.action = action ?: ""
this.target = target this.targetUin = target
this.operator = operator this.operatorUin = operator
this.suffix = suffix ?: "" this.suffix = suffix ?: ""
this.actionImage = actionImg ?: "" this.actionImage = actionImg ?: ""
}.build() }.build()
@ -247,10 +249,10 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupCode: Long, groupCode: Long,
operator: Long, operator: Long,
operatorUid: String, operatorUid: String,
type: GroupMemberIncreasedType type: GroupMemberIncreasedNotice.GroupMemberIncreasedType
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_INCREASE this.type = NoticeEvent.NoticeType.GROUP_MEMBER_INCREASE
this.time = time.toInt() this.time = time.toInt()
this.groupMemberIncrease = GroupMemberIncreasedNotice.newBuilder().apply { this.groupMemberIncrease = GroupMemberIncreasedNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
@ -271,10 +273,10 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupCode: Long, groupCode: Long,
operator: Long, operator: Long,
operatorUid: String, operatorUid: String,
type: GroupMemberDecreasedType type: GroupMemberDecreasedNotice.GroupMemberDecreasedType
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_INCREASE this.type = NoticeEvent.NoticeType.GROUP_MEMBER_INCREASE
this.time = time.toInt() this.time = time.toInt()
this.groupMemberDecrease = GroupMemberDecreasedNotice.newBuilder().apply { this.groupMemberDecrease = GroupMemberDecreasedNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
@ -296,9 +298,9 @@ internal object GlobalEventTransmitter : QQInterfaces() {
setAdmin: Boolean setAdmin: Boolean
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_ADMIN_CHANGED this.type = NoticeEvent.NoticeType.GROUP_ADMIN_CHANGED
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.groupAdminChanged = GroupAdminChangedNotice.newBuilder().apply { this.groupAdminChange = GroupAdminChangedNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
this.targetUid = targetUid this.targetUid = targetUid
this.targetUin = target this.targetUin = target
@ -315,12 +317,12 @@ internal object GlobalEventTransmitter : QQInterfaces() {
isOpen: Boolean isOpen: Boolean
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_WHOLE_BAN this.type = NoticeEvent.NoticeType.GROUP_WHOLE_BAN
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.groupWholeBan = GroupWholeBanNotice.newBuilder().apply { this.groupWholeBan = GroupWholeBanNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
this.isWholeBan = isOpen this.isBan = isOpen
this.operator = operator this.operatorUin = operator
}.build() }.build()
}.build()) }.build())
return true return true
@ -336,17 +338,17 @@ internal object GlobalEventTransmitter : QQInterfaces() {
duration: Int duration: Int
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_BANNED this.type = NoticeEvent.NoticeType.GROUP_MEMBER_BANNED
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.groupMemberBanned = GroupMemberBannedNotice.newBuilder().apply { this.groupMemberBan = GroupMemberBanNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
this.operatorUid = operatorUid this.operatorUid = operatorUid
this.operatorUin = operator this.operatorUin = operator
this.targetUid = targetUid this.targetUid = targetUid
this.targetUin = target this.targetUin = target
this.duration = duration this.duration = duration
this.type = if (duration > 0) GroupMemberBanType.BAN this.type = if (duration > 0) GroupMemberBanNotice.GroupMemberBanType.BAN
else GroupMemberBanType.LIFT_BAN else GroupMemberBanNotice.GroupMemberBanType.LIFT_BAN
}.build() }.build()
}.build()) }.build())
return true return true
@ -363,7 +365,7 @@ internal object GlobalEventTransmitter : QQInterfaces() {
tipText: String tipText: String
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_RECALL this.type = NoticeEvent.NoticeType.GROUP_RECALL
this.time = time.toInt() this.time = time.toInt()
this.groupRecall = GroupRecallNotice.newBuilder().apply { this.groupRecall = GroupRecallNotice.newBuilder().apply {
this.groupId = groupCode this.groupId = groupCode
@ -371,7 +373,7 @@ internal object GlobalEventTransmitter : QQInterfaces() {
this.operatorUin = operator this.operatorUin = operator
this.targetUid = targetUid this.targetUid = targetUid
this.targetUin = target this.targetUin = target
this.messageId = msgId this.messageId = msgId.toString()
this.tipText = tipText this.tipText = tipText
}.build() }.build()
}.build()) }.build())
@ -381,11 +383,18 @@ internal object GlobalEventTransmitter : QQInterfaces() {
suspend fun transCardChange( suspend fun transCardChange(
time: Long, time: Long,
targetId: Long, targetId: Long,
oldCard: String,
newCard: String, newCard: String,
groupId: Long groupId: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeEvent.NoticeType.GROUP_CARD_CHANGED
this.time = time.toInt()
this.groupCardChanged = GroupCardChangedNotice.newBuilder().apply {
this.groupId = groupId
this.targetUin = targetId
this.newCard = newCard
}.build()
}.build())
return true return true
} }
@ -396,7 +405,7 @@ internal object GlobalEventTransmitter : QQInterfaces() {
groupId: Long groupId: Long
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_UNIQUE_TITLE_CHANGED this.type = NoticeEvent.NoticeType.GROUP_MEMBER_UNIQUE_TITLE_CHANGED
this.time = time.toInt() this.time = time.toInt()
this.groupMemberUniqueTitleChanged = GroupUniqueTitleChangedNotice.newBuilder().apply { this.groupMemberUniqueTitleChanged = GroupUniqueTitleChangedNotice.newBuilder().apply {
this.groupId = groupId this.groupId = groupId
@ -416,13 +425,13 @@ internal object GlobalEventTransmitter : QQInterfaces() {
subType: UInt subType: UInt
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_ESSENCE_CHANGED this.type = NoticeEvent.NoticeType.GROUP_ESSENCE_CHANGED
this.time = time.toInt() this.time = time.toInt()
this.groupEssenceChanged = EssenceMessageNotice.newBuilder().apply { this.groupEssenceChanged = GroupEssenceMessageNotice.newBuilder().apply {
this.groupId = groupId this.groupId = groupId
this.messageId = msgId this.messageId = msgId.toString()
this.sender = senderUin this.targetUin = senderUin
this.operator = operatorUin this.operatorUin = operatorUin
this.subType = subType.toInt() this.subType = subType.toInt()
}.build() }.build()
}.build()) }.build())
@ -443,12 +452,11 @@ internal object GlobalEventTransmitter : QQInterfaces() {
actionImg: String? actionImg: String?
): Boolean { ): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_POKE this.type = NoticeEvent.NoticeType.FRIEND_POKE
this.time = msgTime.toInt() this.time = msgTime.toInt()
this.friendPoke = FriendPokeNotice.newBuilder().apply { this.friendPoke = FriendPokeNotice.newBuilder().apply {
this.action = action ?: "" this.action = action ?: ""
this.target = target this.operatorUin = operator
this.operator = operator
this.suffix = suffix ?: "" this.suffix = suffix ?: ""
this.actionImage = actionImg ?: "" this.actionImage = actionImg ?: ""
}.build() }.build()
@ -458,11 +466,11 @@ internal object GlobalEventTransmitter : QQInterfaces() {
suspend fun transPrivateRecall(time: Long, operator: Long, msgId: Long, tipText: String): Boolean { suspend fun transPrivateRecall(time: Long, operator: Long, msgId: Long, tipText: String): Boolean {
pushNotice(NoticeEvent.newBuilder().apply { pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_RECALL this.type = NoticeEvent.NoticeType.FRIEND_RECALL
this.time = time.toInt() this.time = time.toInt()
this.friendRecall = FriendRecallNotice.newBuilder().apply { this.friendRecall = FriendRecallNotice.newBuilder().apply {
this.operator = operator this.operatorUin = operator
this.messageId = msgId this.messageId = msgId.toString()
this.tipText = tipText this.tipText = tipText
}.build() }.build()
}.build()) }.build())
@ -477,7 +485,7 @@ internal object GlobalEventTransmitter : QQInterfaces() {
object RequestTransmitter { object RequestTransmitter {
suspend fun transFriendApp(time: Long, operator: Long, tipText: String, flag: String): Boolean { suspend fun transFriendApp(time: Long, operator: Long, tipText: String, flag: String): Boolean {
pushRequest(RequestsEvent.newBuilder().apply { pushRequest(RequestsEvent.newBuilder().apply {
this.type = RequestType.FRIEND_APPLY this.type = RequestsEvent.RequestType.FRIEND_APPLY
this.time = time.toInt() this.time = time.toInt()
this.friendApply = FriendApplyRequest.newBuilder().apply { this.friendApply = FriendApplyRequest.newBuilder().apply {
this.applierUin = operator this.applierUin = operator
@ -490,30 +498,28 @@ internal object GlobalEventTransmitter : QQInterfaces() {
suspend fun transGroupApply( suspend fun transGroupApply(
time: Long, time: Long,
applier: Long, applierUin: Long,
applierUid: String, applierUid: String,
reason: String, reason: String,
groupCode: Long, groupCode: Long,
flag: String, flag: String
type: GroupApplyType
): Boolean { ): Boolean {
pushRequest(RequestsEvent.newBuilder().apply { pushRequest(RequestsEvent.newBuilder().apply {
this.type = RequestType.GROUP_APPLY this.type = RequestsEvent.RequestType.GROUP_APPLY
this.time = time.toInt() this.time = time.toInt()
this.groupApply = GroupApplyRequest.newBuilder().apply { this.groupApply = GroupApplyRequest.newBuilder().apply {
this.applierUid = applierUid this.applierUid = applierUid
this.applierUin = applier this.applierUin = applierUin
this.groupId = groupCode this.groupId = groupCode
this.reason = reason this.reason = reason
this.flag = flag this.flag = flag
this.type = type
}.build() }.build()
}.build()) }.build())
return true return true
} }
} }
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) { suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, PushMessageBody>>) {
MessageEventFlow.collect { MessageEventFlow.collect {
GlobalScope.launch { GlobalScope.launch {
collector.emit(it) collector.emit(it)

View File

@ -2,7 +2,7 @@ package qq.service.contact
import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import io.kritor.message.Scene import io.kritor.common.Scene
suspend fun Contact.longPeer(): Long { suspend fun Contact.longPeer(): Long {
return when(this.chatType) { return when(this.chatType) {
@ -12,7 +12,7 @@ suspend fun Contact.longPeer(): Long {
} }
} }
suspend fun io.kritor.message.Contact.longPeer(): Long { suspend fun io.kritor.common.Contact.longPeer(): Long {
return when(this.scene) { return when(this.scene) {
Scene.GROUP -> peer.toLong() Scene.GROUP -> peer.toLong()
Scene.FRIEND, Scene.STRANGER, Scene.STRANGER_FROM_GROUP -> if (peer.startsWith("u_")) ContactHelper.getUinByUidAsync(peer).toLong() else peer.toLong() Scene.FRIEND, Scene.STRANGER, Scene.STRANGER_FROM_GROUP -> if (peer.startsWith("u_")) ContactHelper.getUinByUidAsync(peer).toLong() else peer.toLong()

View File

@ -5,11 +5,7 @@ package qq.service.file
import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.mobileqq.pb.ByteStringMicro
import io.grpc.Status import io.grpc.Status
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import io.kritor.file.File import io.kritor.file.*
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.Level
import moe.fuqiuluo.shamrock.helper.LogCenter import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
@ -77,7 +73,7 @@ internal object GroupFileHelper: QQInterfaces() {
}.build() }.build()
} }
suspend fun getGroupFiles(groupId: Long, folderId: String = "/"): GetFilesResponse { suspend fun getGroupFiles(groupId: Long, folderId: String = "/"): GetFileListResponse {
val fileSystemInfo = getGroupFileSystemInfo(groupId) val fileSystemInfo = getGroupFileSystemInfo(groupId)
val fromServiceMsg = sendOidbAW("OidbSvc.0x6d8_1", 1752, 1, oidb_0x6d8.ReqBody().also { val fromServiceMsg = sendOidbAW("OidbSvc.0x6d8_1", 1752, 1, oidb_0x6d8.ReqBody().also {
it.file_list_info_req.set(oidb_0x6d8.GetFileListReqBody().apply { it.file_list_info_req.set(oidb_0x6d8.GetFileListReqBody().apply {
@ -150,7 +146,7 @@ internal object GroupFileHelper: QQInterfaces() {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response")) throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response"))
} }
return GetFilesResponse.newBuilder().apply { return GetFileListResponse.newBuilder().apply {
this.addAllFiles(files) this.addAllFiles(files)
this.addAllFolders(folders) this.addAllFolders(folders)
}.build() }.build()

View File

@ -60,7 +60,12 @@ object AioListener: SimpleKernelMsgListener() {
LogCenter.log("私聊临时消息(private = ${record.senderUin}, groupId=$groupCode)") LogCenter.log("私聊临时消息(private = ${record.senderUin}, groupId=$groupCode)")
if (!GlobalEventTransmitter.MessageTransmitter.transTempMessage(record, record.elements, groupCode, fromNick) if (!GlobalEventTransmitter.MessageTransmitter.transTempMessage(
record,
record.elements,
groupCode,
fromNick
)
) { ) {
LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN) LogCenter.log("私聊临时消息推送失败 -> MessageTransmitter", Level.WARN)
} }
@ -137,4 +142,10 @@ object AioListener: SimpleKernelMsgListener() {
LogCenter.log("群聊文件消息推送失败 -> FileNoticeTransmitter", Level.WARN) LogCenter.log("群聊文件消息推送失败 -> FileNoticeTransmitter", Level.WARN)
} }
} }
@OptIn(ExperimentalStdlibApi::class)
override fun onRecvSysMsg(arrayList: ArrayList<Byte>?) {
LogCenter.log("onRecvSysMsg")
LogCenter.log(arrayList?.toByteArray()?.toHexString() ?: "")
}
} }

View File

@ -5,9 +5,8 @@ import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qphone.base.remote.FromServiceMsg import com.tencent.qphone.base.remote.FromServiceMsg
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.event.GroupApplyType import io.kritor.event.GroupMemberDecreasedNotice.GroupMemberDecreasedType
import io.kritor.event.GroupMemberDecreasedType import io.kritor.event.GroupMemberIncreasedNotice.GroupMemberIncreasedType
import io.kritor.event.GroupMemberIncreasedType
import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -595,7 +594,7 @@ internal object PrimitiveListener {
} }
LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq") LogCenter.log("入群申请($groupCode) $applier: \"$reason\", seq: $msgSeq")
if (!GlobalEventTransmitter.RequestTransmitter if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, applier, applierUid, reason, groupCode, flag, GroupApplyType.GROUP_APPLY_ADD) .transGroupApply(time, applier, applierUid, reason, groupCode, flag)
) { ) {
LogCenter.log("入群申请推送失败!", Level.WARN) LogCenter.log("入群申请推送失败!", Level.WARN)
} }
@ -630,7 +629,7 @@ internal object PrimitiveListener {
} }
LogCenter.log("邀请入群申请($groupCode): $applier") LogCenter.log("邀请入群申请($groupCode): $applier")
if (!GlobalEventTransmitter.RequestTransmitter if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, applier, applierUid, "", groupCode, flag, GroupApplyType.GROUP_APPLY_ADD) .transGroupApply(time, applier, applierUid, "", groupCode, flag)
) { ) {
LogCenter.log("邀请入群申请推送失败!", Level.WARN) LogCenter.log("邀请入群申请推送失败!", Level.WARN)
} }
@ -658,7 +657,7 @@ internal object PrimitiveListener {
"$time;$groupCode;$uin" "$time;$groupCode;$uin"
} }
if (!GlobalEventTransmitter.RequestTransmitter if (!GlobalEventTransmitter.RequestTransmitter
.transGroupApply(time, invitor, invitorUid, "", groupCode, flag, GroupApplyType.GROUP_APPLY_INVITE) .transGroupApply(time, invitor, invitorUid, "", groupCode, flag)
) { ) {
LogCenter.log("邀请入群推送失败!", Level.WARN) LogCenter.log("邀请入群推送失败!", Level.WARN)
} }

View File

@ -1,14 +1,15 @@
package qq.service.msg package qq.service.msg
import com.tencent.mobileqq.qroute.QRoute import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.grpc.Status import io.grpc.Status
import io.grpc.StatusRuntimeException import io.grpc.StatusRuntimeException
import io.kritor.message.ForwardElement import io.kritor.common.ForwardElement
import io.kritor.message.ForwardMessageBody import io.kritor.common.ForwardMessageBody
import io.kritor.message.Scene import io.kritor.common.Scene
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
@ -30,9 +31,7 @@ import kotlin.time.Duration.Companion.seconds
internal object ForwardMessageHelper : QQInterfaces() { internal object ForwardMessageHelper : QQInterfaces() {
suspend fun uploadMultiMsg( suspend fun uploadMultiMsg(
chatType: Int, contact: Contact,
peerId: String,
fromId: String = peerId,
messages: List<ForwardMessageBody>, messages: List<ForwardMessageBody>,
): Result<ForwardElement> { ): Result<ForwardElement> {
var i = -1 var i = -1
@ -41,20 +40,8 @@ internal object ForwardMessageHelper : QQInterfaces() {
val msgs = messages.mapNotNull { msg -> val msgs = messages.mapNotNull { msg ->
kotlin.runCatching { kotlin.runCatching {
val contact = msg.contact.let { when (msg.forwardMessageCase) {
MessageHelper.generateContact( ForwardMessageBody.ForwardMessageCase.MESSAGE_ID -> {
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
)
}
if (msg.hasMessageId()) {
val record: MsgRecord = withTimeoutOrNull(5000) { val record: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java) val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation -> suspendCancellableCoroutine { continuation ->
@ -90,7 +77,7 @@ internal object ForwardMessageHelper : QQInterfaces() {
msgType = when (record.chatType) { msgType = when (record.chatType) {
MsgConstant.KCHATTYPEC2C -> 9 MsgConstant.KCHATTYPEC2C -> 9
MsgConstant.KCHATTYPEGROUP -> 82 MsgConstant.KCHATTYPEGROUP -> 82
else -> throw UnsupportedOperationException("Unsupported chatType: $chatType") else -> throw UnsupportedOperationException("Unsupported chatType: ${contact.chatType}")
}, },
msgSubType = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null, msgSubType = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
divSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null, divSeq = if (record.chatType == MsgConstant.KCHATTYPEC2C) 175 else null,
@ -117,14 +104,17 @@ internal object ForwardMessageHelper : QQInterfaces() {
}.getOrThrow().second }.getOrThrow().second
) )
) )
} else { }
ForwardMessageBody.ForwardMessageCase.MESSAGE -> {
val _msg = msg.message
PushMsgBody( PushMsgBody(
msgHead = if (msg.hasSender()) ResponseHead( msgHead = if (_msg.hasSender()) ResponseHead(
peer = if (msg.sender.hasUin()) msg.sender.uin else TicketHelper.getUin().toLong(), peer = if (_msg.sender.hasUin()) _msg.sender.uin else TicketHelper.getUin().toLong(),
peerUid = msg.sender.uid, peerUid = _msg.sender.uid,
receiverUid = TicketHelper.getUid(), receiverUid = TicketHelper.getUid(),
forward = ResponseForward( forward = ResponseForward(
friendName = if (msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname() friendName = if (_msg.sender.hasNick()) _msg.sender.nick else TicketHelper.getNickname()
) )
) else ResponseHead( ) else ResponseHead(
peer = TicketHelper.getUin().toLong(), peer = TicketHelper.getUin().toLong(),
@ -139,12 +129,12 @@ internal object ForwardMessageHelper : QQInterfaces() {
msgSubType = 175, msgSubType = 175,
divSeq = 175, divSeq = 175,
msgViaRandom = Random.nextLong(), msgViaRandom = Random.nextLong(),
sequence = msg.messageSeq.toLong(), sequence = _msg.messageSeq,
msgTime = msg.messageTime.toLong(), msgTime = _msg.time.toLong(),
u2 = 1, u2 = 1,
u6 = 0, u6 = 0,
u7 = 0, u7 = 0,
msgSeq = msg.messageSeq.toLong(), msgSeq = _msg.messageSeq,
forwardHead = ForwardHead( forwardHead = ForwardHead(
u1 = 0, u1 = 0,
u2 = 0, u2 = 0,
@ -154,15 +144,18 @@ internal object ForwardMessageHelper : QQInterfaces() {
) )
), ),
body = MsgBody( body = MsgBody(
richText = msg.elementsList.toRichText(contact).onSuccess { richText = _msg.elementsList.toRichText(contact).onSuccess {
desc[++i] = desc[++i] =
(if (msg.hasSender() && msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname()) + ": " + it.first (if (_msg.hasSender() && _msg.sender.hasNick()) _msg.sender.nick else TicketHelper.getNickname()) + ": " + it.first
}.onFailure { }.onFailure {
error("消息合成失败: ${it.stackTraceToString()}") error("消息合成失败: ${it.stackTraceToString()}")
}.getOrThrow().second }.getOrThrow().second
) )
) )
} }
else -> null
}
}.onFailure { }.onFailure {
LogCenter.log("消息节点解析失败:${it.stackTraceToString()}", Level.WARN) LogCenter.log("消息节点解析失败:${it.stackTraceToString()}", Level.WARN)
}.getOrNull() }.getOrNull()
@ -192,21 +185,21 @@ internal object ForwardMessageHelper : QQInterfaces() {
) )
val req = LongMsgReq( val req = LongMsgReq(
sendInfo = when (chatType) { sendInfo = when (contact.chatType) {
MsgConstant.KCHATTYPEC2C -> SendLongMsgInfo( MsgConstant.KCHATTYPEC2C -> SendLongMsgInfo(
type = 1, type = 1,
uid = LongMsgUid(if (peerId.startsWith("u_")) peerId else ContactHelper.getUidByUinAsync(peerId.toLong())), uid = LongMsgUid(contact.peerUid),
payload = DeflateTools.gzip(payload.toByteArray()) payload = DeflateTools.gzip(payload.toByteArray())
) )
MsgConstant.KCHATTYPEGROUP -> SendLongMsgInfo( MsgConstant.KCHATTYPEGROUP -> SendLongMsgInfo(
type = 3, type = 3,
uid = LongMsgUid(fromId), uid = LongMsgUid(contact.peerUid),
groupUin = fromId.toULong(), groupUin = contact.peerUid.toULong(),
payload = DeflateTools.gzip(payload.toByteArray()) payload = DeflateTools.gzip(payload.toByteArray())
) )
else -> throw UnsupportedOperationException("Unsupported chatType: $chatType") else -> throw UnsupportedOperationException("Unsupported chatType: ${contact.chatType}")
}, },
setting = LongMsgSettings( setting = LongMsgSettings(
field1 = 4, field1 = 4,

View File

@ -5,7 +5,7 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.message.* import io.kritor.common.*
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.ActionMsgException import moe.fuqiuluo.shamrock.helper.ActionMsgException
@ -135,8 +135,8 @@ private object MsgConvertor {
val elem = Element.newBuilder() val elem = Element.newBuilder()
elem.setImage(ImageElement.newBuilder().apply { elem.setImage(ImageElement.newBuilder().apply {
this.file = md5 this.fileMd5 = md5
this.url = when (record.chatType) { this.fileUrl = when (record.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
originalUrl = originalUrl, originalUrl = originalUrl,
md5 = md5, md5 = md5,
@ -175,7 +175,7 @@ private object MsgConvertor {
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}") else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
} }
this.type = this.type =
if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON if (image.isFlashPic == true) ImageElement.ImageType.FLASH else if (image.original) ImageElement.ImageType.ORIGIN else ImageElement.ImageType.COMMON
this.subType = image.picSubType this.subType = image.picSubType
}) })
@ -191,7 +191,7 @@ private object MsgConvertor {
else ptt.md5HexStr else ptt.md5HexStr
elem.setVoice(VoiceElement.newBuilder().apply { elem.setVoice(VoiceElement.newBuilder().apply {
this.url = when (record.chatType) { this.fileUrl = when (record.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl( MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
"0", "0",
@ -201,7 +201,7 @@ private object MsgConvertor {
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}") else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
} }
this.file = md5 this.fileMd5 = md5
this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE
}) })
@ -219,8 +219,8 @@ private object MsgConvertor {
} }
} else video.fileName.split(".")[0].hex2ByteArray() } else video.fileName.split(".")[0].hex2ByteArray()
elem.setVideo(VideoElement.newBuilder().apply { elem.setVideo(VideoElement.newBuilder().apply {
this.file = md5.toHexString() this.fileMd5 = md5.toHexString()
this.url = when (record.chatType) { this.fileUrl = when (record.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
@ -304,9 +304,9 @@ private object MsgConvertor {
} }
if (sourceRecords.isNullOrEmpty()) { if (sourceRecords.isNullOrEmpty()) {
LogCenter.log("无法查询到回复的消息ID: seq = $msgSeq", Level.WARN) LogCenter.log("无法查询到回复的消息ID: seq = $msgSeq", Level.WARN)
this.messageId = reply.replayMsgId this.messageId = reply.replayMsgId.toString()
} else { } else {
this.messageId = sourceRecords.first().msgId this.messageId = sourceRecords.first().msgId.toString()
} }
}) })
return Result.success(elem.build()) return Result.success(elem.build())

View File

@ -4,7 +4,9 @@ package qq.service.msg
import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import io.kritor.message.* import io.kritor.common.*
import io.kritor.common.Element.ElementType
import io.kritor.common.ImageElement.ImageType
import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt import kotlinx.io.core.readUInt
@ -62,9 +64,9 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
kritorMessages.add(Element.newBuilder().apply { kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.IMAGE this.type = ElementType.IMAGE
this.image = ImageElement.newBuilder().apply { this.image = ImageElement.newBuilder().apply {
this.fileName = md5 this.fileMd5 = md5
this.type = if (customFace.origin == true) ImageType.ORIGIN else ImageType.COMMON this.type = if (customFace.origin == true) ImageType.ORIGIN else ImageType.COMMON
this.url = when (contact.chatType) { this.fileUrl = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
origUrl, origUrl,
md5 md5
@ -82,9 +84,9 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
kritorMessages.add(Element.newBuilder().apply { kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.IMAGE this.type = ElementType.IMAGE
this.image = ImageElement.newBuilder().apply { this.image = ImageElement.newBuilder().apply {
this.fileName = md5 this.fileMd5 = md5
this.type = if (element.notOnlineImage?.original == true) ImageType.ORIGIN else ImageType.COMMON this.type = if (element.notOnlineImage?.original == true) ImageType.ORIGIN else ImageType.COMMON
this.url = when (contact.chatType) { this.fileUrl = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
origUrl, origUrl,
md5 md5
@ -112,7 +114,7 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
kritorMessages.add(Element.newBuilder().apply { kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.REPLY this.type = ElementType.REPLY
this.reply = ReplyElement.newBuilder().apply { this.reply = ReplyElement.newBuilder().apply {
this.messageId = msgId this.messageId = msgId.toString()
}.build() }.build()
}.build()) }.build())
} else if (element.lightApp != null) { } else if (element.lightApp != null) {
@ -256,7 +258,7 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
}) })
}.build() }.build()
}) })
this.applicationId = buttonExtra.field1?.appid?.toLong() ?: 0L this.botAppid = buttonExtra.field1?.appid?.toLong() ?: 0L
}.build()).build() }.build()).build()
) )
} }

View File

@ -1,7 +1,6 @@
package qq.service.msg package qq.service.msg
import android.graphics.BitmapFactory import android.graphics.BitmapFactory
import android.util.Base64
import androidx.exifinterface.media.ExifInterface import androidx.exifinterface.media.ExifInterface
import com.tencent.mobileqq.emoticon.QQSysFaceUtil import com.tencent.mobileqq.emoticon.QQSysFaceUtil
import com.tencent.mobileqq.pb.ByteStringMicro import com.tencent.mobileqq.pb.ByteStringMicro
@ -9,17 +8,18 @@ import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qphone.base.remote.ToServiceMsg import com.tencent.qphone.base.remote.ToServiceMsg
import com.tencent.qqnt.aio.adapter.api.IAIOPttApi import com.tencent.qqnt.aio.adapter.api.IAIOPttApi
import com.tencent.qqnt.kernel.nativeinterface.* import com.tencent.qqnt.kernel.nativeinterface.*
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.FaceElement
import com.tencent.qqnt.kernel.nativeinterface.MarkdownElement
import com.tencent.qqnt.kernel.nativeinterface.MarketFaceElement
import com.tencent.qqnt.kernel.nativeinterface.ReplyElement
import com.tencent.qqnt.kernel.nativeinterface.TextElement
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.message.AtElement import io.kritor.common.*
import io.kritor.message.Button import io.kritor.common.Element.ElementType
import io.kritor.message.Element import io.kritor.common.ImageElement.ImageType
import io.kritor.message.ElementType import io.kritor.common.MusicElement.MusicPlatform
import io.kritor.message.ElementType.* import io.kritor.common.VideoElement
import io.kritor.message.ImageElement
import io.kritor.message.ImageType
import io.kritor.message.MusicPlatform
import io.kritor.message.Scene
import io.kritor.message.VoiceElement
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.config.EnableOldBDH import moe.fuqiuluo.shamrock.config.EnableOldBDH
@ -77,27 +77,27 @@ private typealias NtConvertor = suspend (Contact, Long, Element) -> Result<MsgEl
object NtMsgConvertor { object NtMsgConvertor {
private val ntConvertors = mapOf<ElementType, NtConvertor>( private val ntConvertors = mapOf<ElementType, NtConvertor>(
TEXT to ::textConvertor, ElementType.TEXT to ::textConvertor,
AT to ::atConvertor, ElementType.AT to ::atConvertor,
FACE to ::faceConvertor, ElementType.FACE to ::faceConvertor,
BUBBLE_FACE to ::bubbleFaceConvertor, ElementType.BUBBLE_FACE to ::bubbleFaceConvertor,
REPLY to ::replyConvertor, ElementType.REPLY to ::replyConvertor,
IMAGE to ::imageConvertor, ElementType.IMAGE to ::imageConvertor,
VOICE to ::voiceConvertor, ElementType.VOICE to ::voiceConvertor,
VIDEO to ::videoConvertor, ElementType.VIDEO to ::videoConvertor,
BASKETBALL to ::basketballConvertor, ElementType.BASKETBALL to ::basketballConvertor,
DICE to ::diceConvertor, ElementType.DICE to ::diceConvertor,
RPS to ::rpsConvertor, ElementType.RPS to ::rpsConvertor,
POKE to ::pokeConvertor, ElementType.POKE to ::pokeConvertor,
MUSIC to ::musicConvertor, ElementType.MUSIC to ::musicConvertor,
WEATHER to ::weatherConvertor, ElementType.WEATHER to ::weatherConvertor,
LOCATION to ::locationConvertor, ElementType.LOCATION to ::locationConvertor,
SHARE to ::shareConvertor, ElementType.SHARE to ::shareConvertor,
CONTACT to ::contactConvertor, ElementType.CONTACT to ::contactConvertor,
JSON to ::jsonConvertor, ElementType.JSON to ::jsonConvertor,
FORWARD to ::forwardConvertor, ElementType.FORWARD to ::forwardConvertor,
MARKDOWN to ::markdownConvertor, ElementType.MARKDOWN to ::markdownConvertor,
BUTTON to ::buttonConvertor, ElementType.BUTTON to ::buttonConvertor,
) )
suspend fun convertToNtMsgs(contact: Contact, msgId: Long, msgs: Messages): ArrayList<MsgElement> { suspend fun convertToNtMsgs(contact: Contact, msgId: Long, msgs: Messages): ArrayList<MsgElement> {
@ -135,24 +135,6 @@ object NtMsgConvertor {
val elem = MsgElement() val elem = MsgElement()
val at = TextElement() val at = TextElement()
if (sourceAt.at.accountCase == AtElement.AccountCase.UIN) {
val uin = sourceAt.at.uin
if (uin == 0L) {
at.content = "@全体成员"
at.atType = MsgConstant.ATTYPEALL
at.atNtUid = "0"
} else {
val info = GroupHelper.getTroopMemberInfoByUinV2(contact.peerUid, uin.toString(), true).onFailure {
LogCenter.log("无法获取群成员信息: contact=$contact, id=${uin}", Level.WARN)
}.getOrNull()
at.content = "@${
info?.troopnick.ifNullOrEmpty { info?.friendnick }
?: uin.toString()
}"
at.atType = MsgConstant.ATTYPEONE
at.atNtUid = ContactHelper.getUidByUinAsync(uin)
}
} else {
val uid = sourceAt.at.uid val uid = sourceAt.at.uid
if (uid == "all" || uid == "0") { if (uid == "all" || uid == "0") {
at.content = "@全体成员" at.content = "@全体成员"
@ -170,7 +152,6 @@ object NtMsgConvertor {
at.atType = MsgConstant.ATTYPEONE at.atType = MsgConstant.ATTYPEONE
at.atNtUid = uid at.atNtUid = uid
} }
}
elem.textElement = at elem.textElement = at
elem.elementType = MsgConstant.KELEMTYPETEXT elem.elementType = MsgConstant.KELEMTYPETEXT
return Result.success(elem) return Result.success(elem)
@ -215,7 +196,11 @@ object NtMsgConvertor {
return Result.success(elem) return Result.success(elem)
} }
private suspend fun bubbleFaceConvertor(contact: Contact, msgId: Long, sourceBubbleFace: Element): Result<MsgElement> { private suspend fun bubbleFaceConvertor(
contact: Contact,
msgId: Long,
sourceBubbleFace: Element
): Result<MsgElement> {
val faceId = sourceBubbleFace.bubbleFace.id val faceId = sourceBubbleFace.bubbleFace.id
val local = QQSysFaceUtil.convertToLocal(faceId) val local = QQSysFaceUtil.convertToLocal(faceId)
val name = QQSysFaceUtil.getFaceDescription(local) val name = QQSysFaceUtil.getFaceDescription(local)
@ -242,7 +227,7 @@ object NtMsgConvertor {
element.elementType = MsgConstant.KELEMTYPEREPLY element.elementType = MsgConstant.KELEMTYPEREPLY
val reply = ReplyElement() val reply = ReplyElement()
reply.replayMsgId = sourceReply.reply.messageId reply.replayMsgId = sourceReply.reply.messageId.toLong()
reply.sourceMsgIdInRecords = reply.replayMsgId reply.sourceMsgIdInRecords = reply.replayMsgId
if (reply.replayMsgId == 0L) { if (reply.replayMsgId == 0L) {
@ -251,7 +236,8 @@ object NtMsgConvertor {
withTimeoutOrNull(3000) { withTimeoutOrNull(3000) {
suspendCancellableCoroutine { suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsByMsgId(contact, arrayListOf(reply.replayMsgId)) { _, _, records -> QRoute.api(IMsgService::class.java)
.getMsgsByMsgId(contact, arrayListOf(reply.replayMsgId)) { _, _, records ->
it.resume(records) it.resume(records)
} }
} }
@ -272,23 +258,29 @@ object NtMsgConvertor {
val isFlash = sourceImage.image.type == ImageType.FLASH val isFlash = sourceImage.image.type == ImageType.FLASH
val file = when (sourceImage.image.dataCase!!) { val file = when (sourceImage.image.dataCase!!) {
ImageElement.DataCase.FILE_NAME -> { ImageElement.DataCase.FILE_NAME -> {
val fileMd5 = sourceImage.image.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase() val fileMd5 = sourceImage.image.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "")
.split(".")[0].lowercase()
FileUtils.getFileByMd5(fileMd5) FileUtils.getFileByMd5(fileMd5)
} }
ImageElement.DataCase.FILE_PATH -> { ImageElement.DataCase.FILE_PATH -> {
val filePath = sourceImage.image.filePath val filePath = sourceImage.image.filePath
File(filePath).inputStream().use { File(filePath).inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
} }
} }
ImageElement.DataCase.FILE_BASE64 -> {
FileUtils.saveFileToCache(ByteArrayInputStream( ImageElement.DataCase.FILE -> {
Base64.decode(sourceImage.image.fileBase64, Base64.DEFAULT) FileUtils.saveFileToCache(
)) ByteArrayInputStream(
sourceImage.image.file.toByteArray()
)
)
} }
ImageElement.DataCase.URL -> {
ImageElement.DataCase.FILE_URL -> {
val tmp = FileUtils.getTmpFile() val tmp = FileUtils.getTmpFile()
if(DownloadUtils.download(sourceImage.image.url, tmp)) { if (DownloadUtils.download(sourceImage.image.fileUrl, tmp)) {
tmp.inputStream().use { tmp.inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
}.also { }.also {
@ -296,16 +288,20 @@ object NtMsgConvertor {
} }
} else { } else {
tmp.delete() tmp.delete()
return Result.failure(LogicException("图片资源下载失败: ${sourceImage.image.url}")) return Result.failure(LogicException("图片资源下载失败: ${sourceImage.image.fileUrl}"))
} }
} }
ImageElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("ImageElement data is not set")) ImageElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("ImageElement data is not set"))
} }
if (EnableOldBDH.get()) { if (EnableOldBDH.get()) {
Transfer with when (contact.chatType) { Transfer with when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid)
MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(contact.longPeer().toString()) MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(
contact.longPeer().toString()
)
MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid)
else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for PictureMsg")) else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for PictureMsg"))
} trans PictureResource(file) } trans PictureResource(file)
@ -362,23 +358,27 @@ object NtMsgConvertor {
private suspend fun voiceConvertor(contact: Contact, msgId: Long, sourceVoice: Element): Result<MsgElement> { private suspend fun voiceConvertor(contact: Contact, msgId: Long, sourceVoice: Element): Result<MsgElement> {
var file = when (sourceVoice.voice.dataCase!!) { var file = when (sourceVoice.voice.dataCase!!) {
VoiceElement.DataCase.FILE_NAME -> { VoiceElement.DataCase.FILE_NAME -> {
val fileMd5 = sourceVoice.voice.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase() val fileMd5 = sourceVoice.voice.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "")
.split(".")[0].lowercase()
FileUtils.getFileByMd5(fileMd5) FileUtils.getFileByMd5(fileMd5)
} }
VoiceElement.DataCase.FILE_PATH -> { VoiceElement.DataCase.FILE_PATH -> {
val filePath = sourceVoice.voice.filePath val filePath = sourceVoice.voice.filePath
File(filePath).inputStream().use { File(filePath).inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
} }
} }
VoiceElement.DataCase.FILE_BASE64 -> {
FileUtils.saveFileToCache(ByteArrayInputStream( VoiceElement.DataCase.FILE -> {
Base64.decode(sourceVoice.voice.fileBase64, Base64.DEFAULT) FileUtils.saveFileToCache(
)) sourceVoice.voice.file.toByteArray().inputStream()
)
} }
VoiceElement.DataCase.URL -> {
VoiceElement.DataCase.FILE_URL -> {
val tmp = FileUtils.getTmpFile() val tmp = FileUtils.getTmpFile()
if(DownloadUtils.download(sourceVoice.voice.url, tmp)) { if (DownloadUtils.download(sourceVoice.voice.fileUrl, tmp)) {
tmp.inputStream().use { tmp.inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
}.also { }.also {
@ -386,9 +386,10 @@ object NtMsgConvertor {
} }
} else { } else {
tmp.delete() tmp.delete()
return Result.failure(LogicException("音频资源下载失败: ${sourceVoice.voice.url}")) return Result.failure(LogicException("音频资源下载失败: ${sourceVoice.voice.fileUrl}"))
} }
} }
VoiceElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("VoiceElement data is not set")) VoiceElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("VoiceElement data is not set"))
} }
@ -439,7 +440,10 @@ object NtMsgConvertor {
if (EnableOldBDH.get()) { if (EnableOldBDH.get()) {
if (!(Transfer with when (contact.chatType) { if (!(Transfer with when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid)
MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(contact.longPeer().toString()) MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(
contact.longPeer().toString()
)
MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid)
else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for VoiceMsg")) else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for VoiceMsg"))
} trans VoiceResource(file)) } trans VoiceResource(file))
@ -485,27 +489,31 @@ object NtMsgConvertor {
private suspend fun videoConvertor(contact: Contact, msgId: Long, sourceVideo: Element): Result<MsgElement> { private suspend fun videoConvertor(contact: Contact, msgId: Long, sourceVideo: Element): Result<MsgElement> {
val elem = MsgElement() val elem = MsgElement()
val video = VideoElement() val video = com.tencent.qqnt.kernel.nativeinterface.VideoElement()
val file = when (sourceVideo.video.dataCase!!) { val file = when (sourceVideo.video.dataCase!!) {
io.kritor.message.VideoElement.DataCase.FILE_NAME -> { VideoElement.DataCase.FILE -> {
val fileMd5 = sourceVideo.video.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase() FileUtils.saveFileToCache(
sourceVideo.video.file.toByteArray().inputStream()
)
}
VideoElement.DataCase.FILE_NAME -> {
val fileMd5 = sourceVideo.video.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "")
.split(".")[0].lowercase()
FileUtils.getFileByMd5(fileMd5) FileUtils.getFileByMd5(fileMd5)
} }
io.kritor.message.VideoElement.DataCase.FILE_PATH -> {
VideoElement.DataCase.FILE_PATH -> {
val filePath = sourceVideo.video.filePath val filePath = sourceVideo.video.filePath
File(filePath).inputStream().use { File(filePath).inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
} }
} }
io.kritor.message.VideoElement.DataCase.FILE_BASE64 -> {
FileUtils.saveFileToCache(ByteArrayInputStream( VideoElement.DataCase.FILE_URL -> {
Base64.decode(sourceVideo.video.fileBase64, Base64.DEFAULT)
))
}
io.kritor.message.VideoElement.DataCase.URL -> {
val tmp = FileUtils.getTmpFile() val tmp = FileUtils.getTmpFile()
if(DownloadUtils.download(sourceVideo.video.url, tmp)) { if (DownloadUtils.download(sourceVideo.video.fileUrl, tmp)) {
tmp.inputStream().use { tmp.inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
}.also { }.also {
@ -513,10 +521,11 @@ object NtMsgConvertor {
} }
} else { } else {
tmp.delete() tmp.delete()
return Result.failure(LogicException("视频资源下载失败: ${sourceVideo.video.url}")) return Result.failure(LogicException("视频资源下载失败: ${sourceVideo.video.fileUrl}"))
} }
} }
io.kritor.message.VideoElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("VideoElement data is not set"))
VideoElement.DataCase.DATA_NOT_SET -> return Result.failure(IllegalArgumentException("VideoElement data is not set"))
} }
video.videoMd5 = QQNTWrapperUtil.CppProxy.genFileMd5Hex(file.absolutePath) video.videoMd5 = QQNTWrapperUtil.CppProxy.genFileMd5Hex(file.absolutePath)
@ -543,7 +552,10 @@ object NtMsgConvertor {
if (EnableOldBDH.get()) { if (EnableOldBDH.get()) {
Transfer with when (contact.chatType) { Transfer with when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGROUP -> Troop(contact.peerUid)
MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(contact.longPeer().toString()) MsgConstant.KCHATTYPETEMPC2CFROMGROUP, MsgConstant.KCHATTYPEC2C -> Private(
contact.longPeer().toString()
)
MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid) MsgConstant.KCHATTYPEGUILD -> Troop(contact.peerUid)
else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for VideoMsg")) else -> return Result.failure(Exception("Not supported chatType(${contact.chatType}) for VideoMsg"))
} trans VideoResource(file, File(thumbPath.toString())) } trans VideoResource(file, File(thumbPath.toString()))
@ -567,7 +579,11 @@ object NtMsgConvertor {
return Result.success(elem) return Result.success(elem)
} }
private suspend fun basketballConvertor(contact: Contact, msgId: Long, sourceBasketball: Element): Result<MsgElement> { private suspend fun basketballConvertor(
contact: Contact,
msgId: Long,
sourceBasketball: Element
): Result<MsgElement> {
val elem = MsgElement() val elem = MsgElement()
elem.elementType = MsgConstant.KELEMTYPEFACE elem.elementType = MsgConstant.KELEMTYPEFACE
val face = FaceElement() val face = FaceElement()
@ -697,7 +713,11 @@ object NtMsgConvertor {
} }
private suspend fun locationConvertor(contact: Contact, msgId: Long, sourceLocation: Element): Result<MsgElement> { private suspend fun locationConvertor(contact: Contact, msgId: Long, sourceLocation: Element): Result<MsgElement> {
LbsHelper.tryShareLocation(contact, sourceLocation.location.lat.toDouble(), sourceLocation.location.lon.toDouble()).onFailure { LbsHelper.tryShareLocation(
contact,
sourceLocation.location.lat.toDouble(),
sourceLocation.location.lon.toDouble()
).onFailure {
LogCenter.log("无法发送位置分享", Level.ERROR) LogCenter.log("无法发送位置分享", Level.ERROR)
} }
return Result.failure(ActionMsgException) return Result.failure(ActionMsgException)
@ -801,7 +821,8 @@ object NtMsgConvertor {
val action = button.action val action = button.action
val permission = action.permission val permission = action.permission
return runCatching { return runCatching {
InlineKeyboardButton(button.id, renderData.label, renderData.visitedLabel, renderData.style, InlineKeyboardButton(
button.id, renderData.label, renderData.visitedLabel, renderData.style,
action.type, 0, action.type, 0,
action.unsupportedTips, action.unsupportedTips,
action.data, false, action.data, false,
@ -811,7 +832,8 @@ object NtMsgConvertor {
false, 0, false, arrayListOf() false, 0, false, arrayListOf()
) )
}.getOrElse { }.getOrElse {
InlineKeyboardButton(button.id, renderData.label, renderData.visitedLabel, renderData.style, InlineKeyboardButton(
button.id, renderData.label, renderData.visitedLabel, renderData.style,
action.type, 0, action.type, 0,
action.unsupportedTips, action.unsupportedTips,
action.data, false, action.data, false,

View File

@ -5,7 +5,7 @@ import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.message.* import io.kritor.common.*
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.ActionMsgException import moe.fuqiuluo.shamrock.helper.ActionMsgException
@ -135,8 +135,8 @@ private object ReqMsgConvertor {
val elem = Element.newBuilder() val elem = Element.newBuilder()
elem.setImage(ImageElement.newBuilder().apply { elem.setImage(ImageElement.newBuilder().apply {
this.file = md5 this.fileMd5 = md5
this.url = when (contact.chatType) { this.fileUrl = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl( MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
originalUrl = originalUrl, originalUrl = originalUrl,
md5 = md5, md5 = md5,
@ -175,7 +175,7 @@ private object ReqMsgConvertor {
else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}") else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}")
} }
this.type = this.type =
if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON if (image.isFlashPic == true) ImageElement.ImageType.FLASH else if (image.original) ImageElement.ImageType.ORIGIN else ImageElement.ImageType.COMMON
this.subType = image.picSubType this.subType = image.picSubType
}) })
@ -191,7 +191,7 @@ private object ReqMsgConvertor {
else ptt.md5HexStr else ptt.md5HexStr
elem.setVoice(VoiceElement.newBuilder().apply { elem.setVoice(VoiceElement.newBuilder().apply {
this.url = when (contact.chatType) { this.fileUrl = when (contact.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl( MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
"0", "0",
@ -201,7 +201,7 @@ private object ReqMsgConvertor {
else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}") else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}")
} }
this.file = md5 this.fileMd5 = md5
this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE this.magic = ptt.voiceChangeType != MsgConstant.KPTTVOICECHANGETYPENONE
}) })
@ -219,8 +219,8 @@ private object ReqMsgConvertor {
} }
} else video.fileName.split(".")[0].hex2ByteArray() } else video.fileName.split(".")[0].hex2ByteArray()
elem.setVideo(VideoElement.newBuilder().apply { elem.setVideo(VideoElement.newBuilder().apply {
this.file = md5.toHexString() this.fileMd5 = md5.toHexString()
this.url = when (contact.chatType) { this.fileUrl = when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CVideoDownUrl("0", md5, video.fileUuid)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid) MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
@ -233,7 +233,11 @@ private object ReqMsgConvertor {
suspend fun convertMarketFace(contact: Contact, element: MsgElement): Result<Element> { suspend fun convertMarketFace(contact: Contact, element: MsgElement): Result<Element> {
val marketFace = element.marketFaceElement val marketFace = element.marketFaceElement
val elem = Element.newBuilder() val elem = Element.newBuilder()
return Result.failure(ActionMsgException) elem.setMarketFace(MarketFaceElement.newBuilder().apply {
this.id = marketFace.emojiId
})
// TODO
return Result.success(elem.build())
} }
suspend fun convertStructJson(contact: Contact, element: MsgElement): Result<Element> { suspend fun convertStructJson(contact: Contact, element: MsgElement): Result<Element> {
@ -300,9 +304,9 @@ private object ReqMsgConvertor {
} }
if (sourceRecords.isNullOrEmpty()) { if (sourceRecords.isNullOrEmpty()) {
LogCenter.log("无法查询到回复的消息ID: seq = $msgSeq", Level.WARN) LogCenter.log("无法查询到回复的消息ID: seq = $msgSeq", Level.WARN)
this.messageId = reply.replayMsgId this.messageId = reply.replayMsgId.toString()
} else { } else {
this.messageId = sourceRecords.first().msgId this.messageId = sourceRecords.first().msgId.toString()
} }
}) })
return Result.success(elem.build()) return Result.success(elem.build())
@ -393,7 +397,7 @@ private object ReqMsgConvertor {
}) })
}.build() }.build()
}) })
this.applicationId = inlineKeyboard.botAppid this.botAppid = inlineKeyboard.botAppid
}) })
return Result.success(elem.build()) return Result.success(elem.build())
} }

View File

@ -7,11 +7,8 @@ import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.Contact import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.msg.api.IMsgService import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.message.AtElement import io.kritor.common.Element
import io.kritor.message.Element import io.kritor.common.ImageElement
import io.kritor.message.ElementType
import io.kritor.message.ImageElement
import io.kritor.message.ImageType
import kotlinx.coroutines.suspendCancellableCoroutine import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.Level import moe.fuqiuluo.shamrock.helper.Level
@ -68,7 +65,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
forEach { forEach {
try { try {
when(it.type!!) { when(it.type!!) {
ElementType.TEXT -> { Element.ElementType.TEXT -> {
val text = it.text.text val text = it.text.text
val elem = Elem( val elem = Elem(
text = TextMsg(text) text = TextMsg(text)
@ -76,13 +73,11 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary.append(text) summary.append(text)
} }
ElementType.AT -> { Element.ElementType.AT -> {
when (contact.chatType) { when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> { MsgConstant.KCHATTYPEGROUP -> {
val qq = when (it.at.accountCase) { val qq = ContactHelper.getUinByUidAsync(it.at.uid)
AtElement.AccountCase.UIN -> it.at.uin.toString()
else -> ContactHelper.getUinByUidAsync(it.at.uid)
}
val type: Int val type: Int
val nick = if (it.at.uid == "all" || it.at.uin == 0L) { val nick = if (it.at.uid == "all" || it.at.uin == 0L) {
type = 1 type = 1
@ -112,10 +107,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
} }
MsgConstant.KCHATTYPEC2C -> { MsgConstant.KCHATTYPEC2C -> {
val qq = when (it.at.accountCase) { val qq = ContactHelper.getUinByUidAsync(it.at.uid)
AtElement.AccountCase.UIN -> it.at.uin.toString()
else -> ContactHelper.getUinByUidAsync(it.at.uid)
}
val display = "@" + (ContactHelper.getProfileCard(qq.toLong()).onSuccess { val display = "@" + (ContactHelper.getProfileCard(qq.toLong()).onSuccess {
it.strNick.ifNullOrEmpty { qq } it.strNick.ifNullOrEmpty { qq }
}.onFailure { }.onFailure {
@ -130,7 +122,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
else -> throw UnsupportedOperationException("Unsupported chatType($contact) for AtMsg") else -> throw UnsupportedOperationException("Unsupported chatType($contact) for AtMsg")
} }
} }
ElementType.FACE -> { Element.ElementType.FACE -> {
val faceId = it.face.id val faceId = it.face.id
val elem = if (it.face.isBig) { val elem = if (it.face.isBig) {
Elem( Elem(
@ -159,12 +151,12 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary.append("[表情]") summary.append("[表情]")
} }
ElementType.BUBBLE_FACE -> throw UnsupportedOperationException("Unsupported ElementType.BUBBLE_FACE") Element.ElementType.BUBBLE_FACE -> throw UnsupportedOperationException("Unsupported Element.ElementType.BUBBLE_FACE")
ElementType.REPLY -> { Element.ElementType.REPLY -> {
val msgId = it.reply.messageId val msgId = it.reply.messageId
withTimeoutOrNull(3000) { withTimeoutOrNull(3000) {
suspendCancellableCoroutine { suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsByMsgId(contact, arrayListOf(msgId)) { _, _, records -> QRoute.api(IMsgService::class.java).getMsgsByMsgId(contact, arrayListOf(msgId.toLong())) { _, _, records ->
it.resume(records) it.resume(records)
} }
} }
@ -191,9 +183,9 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
} }
summary.append("[回复消息]") summary.append("[回复消息]")
} }
ElementType.IMAGE -> { Element.ElementType.IMAGE -> {
val type = it.image.type val type = it.image.type
val isOriginal = type == ImageType.ORIGIN val isOriginal = type == ImageElement.ImageType.ORIGIN
val file = when(it.image.dataCase!!) { val file = when(it.image.dataCase!!) {
ImageElement.DataCase.FILE_NAME -> { ImageElement.DataCase.FILE_NAME -> {
val fileMd5 = it.image.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase() val fileMd5 = it.image.fileName.replace(regex = "[{}\\-]".toRegex(), replacement = "").split(".")[0].lowercase()
@ -205,16 +197,16 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
} }
} }
ImageElement.DataCase.FILE_BASE64 -> { ImageElement.DataCase.FILE -> {
FileUtils.saveFileToCache( FileUtils.saveFileToCache(
ByteArrayInputStream( ByteArrayInputStream(
Base64.decode(it.image.fileBase64, Base64.DEFAULT) it.image.file.toByteArray()
) )
) )
} }
ImageElement.DataCase.URL -> { ImageElement.DataCase.FILE_URL -> {
val tmp = FileUtils.getTmpFile() val tmp = FileUtils.getTmpFile()
if(DownloadUtils.download(it.image.url, tmp)) { if(DownloadUtils.download(it.image.fileUrl, tmp)) {
tmp.inputStream().use { tmp.inputStream().use {
FileUtils.saveFileToCache(it) FileUtils.saveFileToCache(it)
}.also { }.also {
@ -222,7 +214,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
} }
} else { } else {
tmp.delete() tmp.delete()
throw LogicException("图片资源下载失败: ${it.image.url}") throw LogicException("图片资源下载失败: ${it.image.fileUrl}")
} }
} }
ImageElement.DataCase.DATA_NOT_SET -> throw IllegalArgumentException("ImageElement data is not set") ImageElement.DataCase.DATA_NOT_SET -> throw IllegalArgumentException("ImageElement data is not set")
@ -352,10 +344,10 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
summary.append("[图片]") summary.append("[图片]")
} }
ElementType.VOICE -> throw UnsupportedOperationException("Unsupported ElementType.VOICE") Element.ElementType.VOICE -> throw UnsupportedOperationException("Unsupported Element.ElementType.VOICE")
ElementType.VIDEO -> throw UnsupportedOperationException("Unsupported ElementType.VIDEO") Element.ElementType.VIDEO -> throw UnsupportedOperationException("Unsupported Element.ElementType.VIDEO")
ElementType.BASKETBALL -> throw UnsupportedOperationException("Unsupported ElementType.BASKETBALL") Element.ElementType.BASKETBALL -> throw UnsupportedOperationException("Unsupported Element.ElementType.BASKETBALL")
ElementType.DICE -> { Element.ElementType.DICE -> {
val elem = Elem( val elem = Elem(
commonElem = CommonElem( commonElem = CommonElem(
serviceType = 37, serviceType = 37,
@ -375,7 +367,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary .append( "[骰子]" ) summary .append( "[骰子]" )
} }
ElementType.RPS -> { Element.ElementType.RPS -> {
val elem = Elem( val elem = Elem(
commonElem = CommonElem( commonElem = CommonElem(
serviceType = 37, serviceType = 37,
@ -395,7 +387,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary .append( "[包剪锤]" ) summary .append( "[包剪锤]" )
} }
ElementType.POKE -> { Element.ElementType.POKE -> {
val elem = Elem( val elem = Elem(
commonElem = CommonElem( commonElem = CommonElem(
serviceType = 2, serviceType = 2,
@ -410,8 +402,8 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary .append( "[戳一戳]" ) summary .append( "[戳一戳]" )
} }
ElementType.MUSIC -> throw UnsupportedOperationException("Unsupported ElementType.MUSIC") Element.ElementType.MUSIC -> throw UnsupportedOperationException("Unsupported Element.ElementType.MUSIC")
ElementType.WEATHER -> { Element.ElementType.WEATHER -> {
var code = it.weather.code.toIntOrNull() var code = it.weather.code.toIntOrNull()
if (code == null) { if (code == null) {
val city = it.weather.city val city = it.weather.city
@ -438,11 +430,11 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
throw LogicException("无法获取城市天气") throw LogicException("无法获取城市天气")
} }
} }
ElementType.LOCATION -> throw UnsupportedOperationException("Unsupported ElementType.LOCATION") Element.ElementType.LOCATION -> throw UnsupportedOperationException("Unsupported Element.ElementType.LOCATION")
ElementType.SHARE -> throw UnsupportedOperationException("Unsupported ElementType.SHARE") Element.ElementType.SHARE -> throw UnsupportedOperationException("Unsupported Element.ElementType.SHARE")
ElementType.GIFT -> throw UnsupportedOperationException("Unsupported ElementType.GIFT") Element.ElementType.GIFT -> throw UnsupportedOperationException("Unsupported Element.ElementType.GIFT")
ElementType.MARKET_FACE -> throw UnsupportedOperationException("Unsupported ElementType.MARKET_FACE") Element.ElementType.MARKET_FACE -> throw UnsupportedOperationException("Unsupported Element.ElementType.MARKET_FACE")
ElementType.FORWARD -> { Element.ElementType.FORWARD -> {
val resId = it.forward.resId val resId = it.forward.resId
val filename = UUID.randomUUID().toString().uppercase() val filename = UUID.randomUUID().toString().uppercase()
var content = it.forward.summary var content = it.forward.summary
@ -496,8 +488,8 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary.append( "[聊天记录]" ) summary.append( "[聊天记录]" )
} }
ElementType.CONTACT -> throw UnsupportedOperationException("Unsupported ElementType.CONTACT") Element.ElementType.CONTACT -> throw UnsupportedOperationException("Unsupported Element.ElementType.CONTACT")
ElementType.JSON -> { Element.ElementType.JSON -> {
val elem = Elem( val elem = Elem(
lightApp = LightAppElem( lightApp = LightAppElem(
data = byteArrayOf(1) + DeflateTools.compress(it.json.json.toByteArray()) data = byteArrayOf(1) + DeflateTools.compress(it.json.json.toByteArray())
@ -506,9 +498,9 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary .append( "[Json消息]" ) summary .append( "[Json消息]" )
} }
ElementType.XML -> throw UnsupportedOperationException("Unsupported ElementType.XML") Element.ElementType.XML -> throw UnsupportedOperationException("Unsupported Element.ElementType.XML")
ElementType.FILE -> throw UnsupportedOperationException("Unsupported ElementType.FILE") Element.ElementType.FILE -> throw UnsupportedOperationException("Unsupported Element.ElementType.FILE")
ElementType.MARKDOWN -> { Element.ElementType.MARKDOWN -> {
val elem = Elem( val elem = Elem(
commonElem = CommonElem( commonElem = CommonElem(
serviceType = 45, serviceType = 45,
@ -519,7 +511,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary.append("[Markdown消息]") summary.append("[Markdown消息]")
} }
ElementType.BUTTON -> { Element.ElementType.BUTTON -> {
val elem = Elem( val elem = Elem(
commonElem = CommonElem( commonElem = CommonElem(
serviceType = 46, serviceType = 46,
@ -552,7 +544,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
) )
}) })
}, },
appid = it.button.applicationId.toULong() appid = it.button.botAppid.toULong()
) )
).toByteArray(), ).toByteArray(),
businessType = 1 businessType = 1
@ -561,8 +553,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
elems.add(elem) elems.add(elem)
summary.append("[Button消息]") summary.append("[Button消息]")
} }
ElementType.NODE -> throw UnsupportedOperationException("Unsupported ElementType.NODE") Element.ElementType.UNRECOGNIZED -> throw UnsupportedOperationException("Unsupported Element.ElementType.UNRECOGNIZED")
ElementType.UNRECOGNIZED -> throw UnsupportedOperationException("Unsupported ElementType.UNRECOGNIZED")
} }
} catch (e: Throwable) { } catch (e: Throwable) {
LogCenter.log("转换消息失败(Multi): ${e.stackTraceToString()}", Level.ERROR) LogCenter.log("转换消息失败(Multi): ${e.stackTraceToString()}", Level.ERROR)