fix kritor

This commit is contained in:
Simplxs 2024-03-24 05:19:50 +08:00
parent b6a510ce05
commit ad313f384c
No known key found for this signature in database
GPG Key ID: E23537FF14DD6507
34 changed files with 1050 additions and 1041 deletions

4
.gitmodules vendored
View File

@ -1,3 +1,3 @@
[submodule "kritor"]
path = kritor
url = https://github.com/KarinJS/kritor-kotlin
path = kritor/kritor
url = https://github.com/KarinJS/kritor

View File

@ -1,7 +1,5 @@
package kritor.service
import kotlin.reflect.KClass
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FUNCTION)
annotation class Grpc(

View File

@ -1,8 +0,0 @@
package moe.fuqiuluo.symbols
@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.CLASS)
annotation class OneBotHandler(
val actionName: String,
val alias: Array<String> = []
)

View File

@ -1,5 +1,5 @@
plugins {
kotlin("jvm") version "1.9.21"
kotlin("jvm") version "1.9.22"
}
repositories {

View File

@ -15,8 +15,9 @@ fun ktor(target: String, name: String): String {
return "io.ktor:ktor-$target-$name:${Versions.ktorVersion}"
}
fun grpc(name: String, version: String) = "io.grpc:grpc-$name:$version"
object Versions {
const val roomVersion = "2.5.0"
const val ktorVersion = "2.3.3"
}

1
kritor

@ -1 +0,0 @@
Subproject commit 10dca646c83d1ef45deab0ac4ab2d80446e902cf

42
kritor/.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/
### IntelliJ IDEA ###
.idea/modules.xml
.idea/jarRepositories.xml
.idea/compiler.xml
.idea/libraries/
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/
### Eclipse ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
### VS Code ###
.vscode/
### Mac OS ###
.DS_Store

75
kritor/build.gradle.kts Normal file
View File

@ -0,0 +1,75 @@
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.google.protobuf") version "0.9.4"
}
android {
namespace = "moe.whitechi73.kritor"
compileSdk = 34
defaultConfig {
minSdk = 24
consumerProguardFiles("consumer-rules.pro")
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = "1.8"
}
}
dependencies {
protobuf(files("kritor/protos"))
implementation(kotlinx("coroutines-core", "1.8.0"))
implementation("com.google.protobuf:protobuf-java:3.25.3")
implementation(grpc("stub", "1.62.2"))
implementation(grpc("kotlin-stub", "1.4.1"))
implementation(grpc("protobuf", "1.62.2"))
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.3"
}
plugins {
create("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.62.2"
}
create("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
create("grpc")
create("grpckt")
}
it.builtins {
create("java")
}
}
}
}
tasks.withType<KotlinCompile>().configureEach {
kotlinOptions.freeCompilerArgs += "-opt-in=kotlin.RequiresOptIn"
}

View File

1
kritor/kritor Submodule

@ -0,0 +1 @@
Subproject commit 94f2e74b79b003685f785336a279d2cc75f95829

21
kritor/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

View File

@ -37,7 +37,6 @@ android {
}
dependencies {
//implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
implementation(kotlinx("serialization-json", "1.6.2"))

View File

@ -13,7 +13,7 @@ data class ButtonExtra(
@Serializable
data class Object1(
@ProtoNumber(1) val rows: List<Row>? = null,
@ProtoNumber(2) val appid: Int? = null,
@ProtoNumber(2) val appid: ULong? = null,
)
@Serializable

View File

@ -26,7 +26,7 @@ buildscript {
}
}
dependencies {
classpath("com.android.tools:r8:8.2.47")
classpath("com.android.tools:r8:8.3.37")
}
}
@ -34,11 +34,9 @@ rootProject.name = "Shamrock"
include(
":app",
":xposed",
":qqinterface"
":qqinterface",
":protobuf",
":processor",
":annotations",
":kritor"
)
include(":protobuf")
include(":processor")
include(":annotations")
include(":kritor")
project(":kritor").projectDir = file("kritor/protos")

View File

@ -5,7 +5,6 @@ plugins {
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("com.google.devtools.ksp") version "1.9.22-1.0.17"
id("com.google.protobuf") version "0.9.4"
kotlin("plugin.serialization") version "1.9.22"
}
@ -61,11 +60,10 @@ kotlin {
}
dependencies {
compileOnly ("de.robv.android.xposed:api:82")
compileOnly (project(":qqinterface"))
protobuf(project(":kritor"))
compileOnly("de.robv.android.xposed:api:82")
compileOnly(project(":qqinterface"))
implementation(project(":kritor"))
implementation(project(":protobuf"))
implementation(project(":annotations"))
ksp(project(":processor"))
@ -75,24 +73,20 @@ dependencies {
DEPENDENCY_ANDROIDX.forEach {
implementation(it)
}
//implementation(DEPENDENCY_PROTOBUF)
implementation(room("runtime"))
kapt(room("compiler"))
implementation(room("ktx"))
implementation(kotlinx("io-jvm", "0.1.16"))
implementation(kotlinx("serialization-protobuf", "1.6.2"))
implementation(ktor("client", "core"))
implementation(ktor("client", "okhttp"))
implementation(ktor("serialization", "kotlinx-json"))
implementation("io.grpc:grpc-stub:1.62.2")
implementation("io.grpc:grpc-protobuf-lite:1.62.2")
implementation("com.google.protobuf:protobuf-kotlin-lite:3.25.3")
implementation("io.grpc:grpc-kotlin-stub:1.4.1")
implementation("io.grpc:grpc-okhttp:1.62.2")
implementation(grpc("protobuf", "1.62.2"))
implementation(grpc("kotlin-stub", "1.4.1"))
implementation(grpc("okhttp", "1.62.2"))
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
@ -106,40 +100,3 @@ tasks.withType<KotlinCompile>().all {
freeCompilerArgs = listOf("-opt-in=kotlin.RequiresOptIn")
}
}
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:3.25.3"
}
plugins {
create("java") {
artifact = "io.grpc:protoc-gen-grpc-java:1.62.2"
}
create("grpc") {
artifact = "io.grpc:protoc-gen-grpc-java:1.62.2"
}
create("grpckt") {
artifact = "io.grpc:protoc-gen-grpc-kotlin:1.4.1:jdk8@jar"
}
}
generateProtoTasks {
all().forEach {
it.plugins {
create("java") {
option("lite")
}
create("grpc") {
option("lite")
}
create("grpckt") {
option("lite")
}
}
it.builtins {
create("kotlin") {
option("lite")
}
}
}
}
}

View File

@ -5,10 +5,7 @@ import com.google.protobuf.ByteString
import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import io.kritor.ReverseServiceGrpcKt
import io.kritor.event.EventServiceGrpcKt
import io.kritor.event.EventType
import io.kritor.event.eventStructure
import io.kritor.event.messageEvent
import io.kritor.event.*
import io.kritor.reverse.ReqCode
import io.kritor.reverse.Request
import io.kritor.reverse.Response
@ -83,23 +80,23 @@ internal class KritorClient(
EventServiceGrpcKt.EventServiceCoroutineStub(channel).registerPassiveListener(channelFlow {
when(eventType) {
EventType.EVENT_TYPE_MESSAGE -> GlobalEventTransmitter.onMessageEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_MESSAGE
this.message = it.second
})
}.build())
}
EventType.EVENT_TYPE_CORE_EVENT -> {}
EventType.EVENT_TYPE_NOTICE -> GlobalEventTransmitter.onNoticeEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_NOTICE
this.notice = it
})
}.build())
}
EventType.EVENT_TYPE_REQUEST -> GlobalEventTransmitter.onRequestEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_REQUEST
this.request = it
})
}.build())
}
EventType.UNRECOGNIZED -> {}
}

View File

@ -8,8 +8,6 @@ import io.kritor.AuthRsp
import io.kritor.AuthenticationGrpcKt
import io.kritor.GetAuthStateReq
import io.kritor.GetAuthStateRsp
import io.kritor.authRsp
import io.kritor.getAuthStateRsp
import kritor.auth.AuthInterceptor
import moe.fuqiuluo.shamrock.config.ActiveTicket
import moe.fuqiuluo.shamrock.config.ShamrockConfig
@ -19,10 +17,10 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl
@Grpc("Authentication", "Auth")
override suspend fun auth(request: AuthReq): AuthRsp {
if (QQInterfaces.app.account != request.account) {
return authRsp {
return AuthRsp.newBuilder().apply {
code = AuthCode.NO_ACCOUNT
msg = "No such account"
}
}.build()
}
val activeTicketName = ActiveTicket.name()
@ -31,26 +29,26 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl
val ticket = ShamrockConfig.getProperty(activeTicketName + if (index == 0) "" else ".$index", null)
if (ticket.isNullOrEmpty()) {
if (index == 0) {
return authRsp {
return AuthRsp.newBuilder().apply {
code = AuthCode.OK
msg = "OK"
}
}.build()
} else {
break
}
} else if (ticket == request.ticket) {
return authRsp {
return AuthRsp.newBuilder().apply {
code = AuthCode.OK
msg = "OK"
}
}.build()
}
index++
}
return authRsp {
return AuthRsp.newBuilder().apply {
code = AuthCode.NO_TICKET
msg = "Invalid ticket"
}
}.build()
}
@Grpc("Authentication", "GetAuthState")
@ -59,8 +57,8 @@ internal object Authentication: AuthenticationGrpcKt.AuthenticationCoroutineImpl
throw StatusRuntimeException(Status.CANCELLED.withDescription("No such account"))
}
return getAuthStateRsp {
return GetAuthStateRsp.newBuilder().apply {
isRequiredAuth = AuthInterceptor.getAllTicket().isNotEmpty()
}
}.build()
}
}

View File

@ -23,31 +23,31 @@ import io.kritor.contact.StrangerInfo
import io.kritor.contact.StrangerInfoRequest
import io.kritor.contact.VoteUserRequest
import io.kritor.contact.VoteUserResponse
import io.kritor.contact.profileCard
import io.kritor.contact.strangerInfo
import io.kritor.contact.voteUserResponse
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import qq.service.QQInterfaces
import qq.service.contact.ContactHelper
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImplBase() {
internal object ContactService : ContactServiceGrpcKt.ContactServiceCoroutineImplBase() {
@Grpc("ContactService", "VoteUser")
override suspend fun voteUser(request: VoteUserRequest): VoteUserResponse {
ContactHelper.voteUser(when(request.accountCase!!) {
VoteUserRequest.AccountCase.ACCOUNT_UIN -> request.accountUin
VoteUserRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong()
VoteUserRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("account not set")
)
}, request.voteCount).onFailure {
throw StatusRuntimeException(Status.INTERNAL
.withDescription(it.stackTraceToString())
ContactHelper.voteUser(
when (request.accountCase!!) {
VoteUserRequest.AccountCase.ACCOUNT_UIN -> request.accountUin
VoteUserRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong()
VoteUserRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("account not set")
)
}, request.voteCount
).onFailure {
throw StatusRuntimeException(
Status.INTERNAL
.withDescription(it.stackTraceToString())
)
}
return voteUserResponse { }
return VoteUserResponse.newBuilder().build()
}
@Grpc("ContactService", "GetProfileCard")
@ -55,21 +55,23 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl
val uin = when (request.accountCase!!) {
ProfileCardRequest.AccountCase.ACCOUNT_UIN -> request.accountUin
ProfileCardRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid).toLong()
ProfileCardRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("account not set")
ProfileCardRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("account not set")
)
}
val contact = ContactHelper.getProfileCard(uin)
contact.onFailure {
throw StatusRuntimeException(Status.INTERNAL
.withDescription(it.stackTraceToString())
throw StatusRuntimeException(
Status.INTERNAL
.withDescription(it.stackTraceToString())
)
}
contact.onSuccess {
return profileCard {
return ProfileCard.newBuilder().apply {
this.uin = it.uin.toLong()
this.uid = if (request.hasAccountUid()) request.accountUid
else ContactHelper.getUidByUinAsync(it.uin.toLong())
@ -81,11 +83,12 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl
this.voteCnt = it.lVoteCount.toInt()
this.qid = it.qid ?: ""
this.isSchoolVerified = it.schoolVerifiedFlag
}
}.build()
}
throw StatusRuntimeException(Status.INTERNAL
.withDescription("logic failed")
throw StatusRuntimeException(
Status.INTERNAL
.withDescription("logic failed")
)
}
@ -93,13 +96,14 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl
override suspend fun getStrangerInfo(request: StrangerInfoRequest): StrangerInfo {
val userId = request.uin
val info = ContactHelper.refreshAndGetProfileCard(userId).onFailure {
throw StatusRuntimeException(Status.INTERNAL
.withCause(it)
.withDescription("Unable to fetch stranger info")
throw StatusRuntimeException(
Status.INTERNAL
.withCause(it)
.withDescription("Unable to fetch stranger info")
)
}.getOrThrow()
return strangerInfo {
return StrangerInfo.newBuilder().apply {
this.uid = ContactHelper.getUidByUinAsync(userId)
this.uin = (info.uin ?: "0").toLong()
this.name = info.strNick ?: ""
@ -108,14 +112,14 @@ internal object ContactService: ContactServiceGrpcKt.ContactServiceCoroutineImpl
this.voteCnt = info.lVoteCount.toInt()
this.qid = info.qid ?: ""
this.isSchoolVerified = info.schoolVerifiedFlag
this.ext = StrangerExt.newBuilder()
.setBigVip(info.bBigClubVipOpen == 1.toByte())
.setHollywoodVip(info.bHollywoodVipOpen == 1.toByte())
.setQqVip(info.bQQVipOpen == 1.toByte())
.setSuperVip(info.bSuperQQOpen == 1.toByte())
.setVoted(info.bVoted == 1.toByte())
.build().toByteString()
}
this.ext = StrangerExt.newBuilder().apply {
this.bigVip = info.bBigClubVipOpen == 1.toByte()
this.hollywoodVip = info.bHollywoodVipOpen == 1.toByte()
this.qqVip = info.bQQVipOpen == 1.toByte()
this.superVip = info.bSuperQQOpen == 1.toByte()
this.voted = info.bVoted == 1.toByte()
}.build().toByteString()
}.build()
}
@Grpc("ContactService", "GetUid")

View File

@ -3,40 +3,31 @@ package kritor.service
import com.google.protobuf.ByteString
import com.tencent.mobileqq.fe.FEKit
import com.tencent.mobileqq.qsec.qsecdandelionsdk.Dandelion
import io.kritor.developer.DeveloperServiceGrpcKt
import io.kritor.developer.EnergyRequest
import io.kritor.developer.EnergyResponse
import io.kritor.developer.SendPacketRequest
import io.kritor.developer.SendPacketResponse
import io.kritor.developer.SignRequest
import io.kritor.developer.SignResponse
import io.kritor.developer.energyResponse
import io.kritor.developer.sendPacketResponse
import io.kritor.developer.signResponse
import io.kritor.developer.*
import qq.service.QQInterfaces
internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCoroutineImplBase() {
@Grpc("DeveloperService", "Sign")
override suspend fun sign(request: SignRequest): SignResponse {
return signResponse {
return SignResponse.newBuilder().apply {
val result = FEKit.getInstance().getSign(request.command, request.buffer.toByteArray(), request.seq, request.uin)
this.sign = ByteString.copyFrom(result.sign)
this.token = ByteString.copyFrom(result.token)
this.extra = ByteString.copyFrom(result.extra)
}
}.build()
}
@Grpc("DeveloperService", "Energy")
override suspend fun energy(request: EnergyRequest): EnergyResponse {
return energyResponse {
return EnergyResponse.newBuilder().apply {
this.result = ByteString.copyFrom(Dandelion.getInstance().fly(request.data, request.salt.toByteArray()))
}
}.build()
}
@Grpc("DeveloperService", "SendPacket")
override suspend fun sendPacket(request: SendPacketRequest): SendPacketResponse {
return sendPacketResponse {
return SendPacketResponse.newBuilder().apply {
val fromServiceMsg = QQInterfaces.sendBufferAW(request.command, request.isProtobuf, request.requestBuffer.toByteArray())
if (fromServiceMsg?.wupBuffer == null) {
this.isSuccess = false
@ -44,7 +35,7 @@ internal object DeveloperService: DeveloperServiceGrpcKt.DeveloperServiceCorouti
this.isSuccess = true
this.responseBuffer = ByteString.copyFrom(fromServiceMsg.wupBuffer)
}
}
}.build()
}
}

View File

@ -2,39 +2,40 @@ package kritor.service
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.event.EventRequest
import io.kritor.event.EventServiceGrpcKt
import io.kritor.event.EventStructure
import io.kritor.event.EventType
import io.kritor.event.RequestPushEvent
import io.kritor.event.eventStructure
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import moe.fuqiuluo.shamrock.internals.GlobalEventTransmitter
internal object EventService: EventServiceGrpcKt.EventServiceCoroutineImplBase() {
internal object EventService : EventServiceGrpcKt.EventServiceCoroutineImplBase() {
override fun registerActiveListener(request: RequestPushEvent): Flow<EventStructure> {
return channelFlow {
when(request.type!!) {
when (request.type!!) {
EventType.EVENT_TYPE_CORE_EVENT -> {}
EventType.EVENT_TYPE_MESSAGE -> GlobalEventTransmitter.onMessageEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_MESSAGE
this.message = it.second
})
}.build())
}
EventType.EVENT_TYPE_NOTICE -> GlobalEventTransmitter.onRequestEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_NOTICE
this.request = it
})
}.build())
}
EventType.EVENT_TYPE_REQUEST -> GlobalEventTransmitter.onNoticeEvent {
send(eventStructure {
send(EventStructure.newBuilder().apply {
this.type = EventType.EVENT_TYPE_NOTICE
this.notice = it
})
}.build())
}
EventType.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT)
}
}

View File

@ -1,50 +1,85 @@
package kritor.service
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.message.Element
import io.kritor.message.ElementType
import io.kritor.message.ForwardMessageRequest
import io.kritor.message.ForwardMessageResponse
import io.kritor.message.ForwardMessageServiceGrpcKt
import io.kritor.message.Scene
import io.kritor.message.element
import io.kritor.message.forwardMessageResponse
import io.kritor.event.MessageEvent
import io.kritor.message.*
import qq.service.contact.longPeer
import qq.service.msg.ForwardMessageHelper
import qq.service.msg.MessageHelper
import qq.service.msg.NtMsgConvertor
import qq.service.msg.toKritorResponseMessages
internal object ForwardMessageService: ForwardMessageServiceGrpcKt.ForwardMessageServiceCoroutineImplBase() {
@Grpc("ForwardMessageService", "ForwardMessage")
override suspend fun forwardMessage(request: ForwardMessageRequest): ForwardMessageResponse {
internal object ForwardMessageService : ForwardMessageServiceGrpcKt.ForwardMessageServiceCoroutineImplBase() {
@Grpc("ForwardMessageService", "UploadForwardMessage")
override suspend fun uploadForwardMessage(request: UploadForwardMessageRequest): UploadForwardMessageResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val forwardMessage = ForwardMessageHelper.uploadMultiMsg(contact.chatType, contact.longPeer().toString(), contact.guildId, request.messagesList).onFailure {
val forwardMessage = ForwardMessageHelper.uploadMultiMsg(
contact.chatType,
contact.longPeer().toString(),
contact.guildId,
request.messagesList
).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow()
val uniseq = MessageHelper.generateMsgId(contact.chatType)
return forwardMessageResponse {
this.messageId = MessageHelper.sendMessage(contact, NtMsgConvertor.convertToNtMsgs(contact, uniseq, arrayListOf(element {
this.type = ElementType.FORWARD
this.forward = forwardMessage
})), request.retryCount, uniseq).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow()
return UploadForwardMessageResponse.newBuilder().apply {
this.resId = forwardMessage.id
}
}.build()
}
@Grpc("ForwardMessageService", "DownloadForwardMessage")
override suspend fun downloadForwardMessage(request: DownloadForwardMessageRequest): DownloadForwardMessageResponse {
return DownloadForwardMessageResponse.newBuilder().apply {
this.addAllMessages(
MessageHelper.getForwardMsg(request.resId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow().map { detail ->
MessageEvent.newBuilder().apply {
this.time = detail.time
this.messageId = detail.qqMsgId
this.messageSeq = detail.msgSeq
this.contact = Contact.newBuilder().apply {
this.scene = when (detail.msgType) {
MsgConstant.KCHATTYPEC2C -> Scene.FRIEND
MsgConstant.KCHATTYPEGROUP -> Scene.GROUP
MsgConstant.KCHATTYPEGUILD -> Scene.GUILD
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Scene.STRANGER_FROM_GROUP
MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN -> Scene.NEARBY
else -> Scene.STRANGER
}
this.peer = detail.peerId.toString()
}.build()
this.sender = Sender.newBuilder().apply {
this.uin = detail.sender.userId
this.nick = detail.sender.nickName
this.uid = detail.sender.uid
}.build()
detail.message?.elements?.toKritorResponseMessages(
com.tencent.qqnt.kernel.nativeinterface.Contact(
detail.msgType,
detail.peerId.toString(),
null
)
)?.let {
this.addAllElements(it)
}
}.build()
}
)
}.build()
}
}

View File

@ -2,12 +2,7 @@ package kritor.service
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.friend.FriendServiceGrpcKt
import io.kritor.friend.GetFriendListRequest
import io.kritor.friend.GetFriendListResponse
import io.kritor.friend.friendData
import io.kritor.friend.friendExt
import io.kritor.friend.getFriendListResponse
import io.kritor.friend.*
import qq.service.contact.ContactHelper
import qq.service.friend.FriendHelper
@ -20,9 +15,9 @@ internal object FriendService: FriendServiceGrpcKt.FriendServiceCoroutineImplBas
)
}.getOrThrow()
return getFriendListResponse {
return GetFriendListResponse.newBuilder().apply {
friendList.forEach {
this.friendList.add(friendData {
this.addFriendList(FriendData.newBuilder().apply {
uin = it.uin.toLong()
uid = ContactHelper.getUidByUinAsync(uin)
qid = ""
@ -32,10 +27,10 @@ internal object FriendService: FriendServiceGrpcKt.FriendServiceCoroutineImplBas
level = 0
gender = it.gender.toInt()
groupId = it.groupid
ext = friendExt {}.toByteString()
ext = FriendExt.newBuilder().build().toByteString()
})
}
}
}.build()
}
}

View File

@ -15,7 +15,6 @@ import qq.service.QQInterfaces
import qq.service.file.GroupFileHelper
import qq.service.file.GroupFileHelper.getGroupFileSystemInfo
import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
import tencent.im.oidb.oidb_sso
internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCoroutineImplBase() {
@ -42,10 +41,10 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
if (rsp.createFolder?.retCode != 0) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to create folder: ${rsp.createFolder?.retCode}"))
}
return createFolderResponse {
return CreateFolderResponse.newBuilder().apply {
this.id = rsp.createFolder?.folderInfo?.folderId ?: ""
this.usedSpace = 0
}
}.build()
}
@Grpc("GroupFileService", "DeleteFolder")
@ -66,7 +65,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
if (rsp.deleteFolder?.retCode != 0) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to delete folder: ${rsp.deleteFolder?.retCode}"))
}
return deleteFolderResponse { }
return DeleteFolderResponse.newBuilder().build()
}
@Grpc("GroupFileService", "DeleteFile")
@ -93,7 +92,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
if (rsp.delete_file_rsp.int32_ret_code.get() != 0) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to delete file: ${rsp.delete_file_rsp.int32_ret_code.get()}"))
}
return deleteFileResponse { }
return DeleteFileResponse.newBuilder().build()
}
@Grpc("GroupFileService", "RenameFolder")
@ -115,7 +114,7 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
if (rsp.renameFolder?.retCode != 0) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to rename folder: ${rsp.renameFolder?.retCode}"))
}
return renameFolderResponse { }
return RenameFolderResponse.newBuilder().build()
}
@Grpc("GroupFileService", "GetFileSystemInfo")
@ -125,11 +124,11 @@ internal object GroupFileService: GroupFileServiceGrpcKt.GroupFileServiceCorouti
@Grpc("GroupFileService", "GetRootFiles")
override suspend fun getRootFiles(request: GetRootFilesRequest): GetRootFilesResponse {
return getRootFilesResponse {
return GetRootFilesResponse.newBuilder().apply {
val response = GroupFileHelper.getGroupFiles(request.groupId)
this.files.addAll(response.filesList)
this.folders.addAll(response.foldersList)
}
this.addAllFiles(response.filesList)
this.addAllFolders(response.foldersList)
}.build()
}
@Grpc("GroupFileService", "GetFiles")

View File

@ -2,212 +2,186 @@ package kritor.service
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.group.BanMemberRequest
import io.kritor.group.BanMemberResponse
import io.kritor.group.GetGroupHonorRequest
import io.kritor.group.GetGroupHonorResponse
import io.kritor.group.GetGroupInfoRequest
import io.kritor.group.GetGroupInfoResponse
import io.kritor.group.GetGroupListRequest
import io.kritor.group.GetGroupListResponse
import io.kritor.group.GetGroupMemberInfoRequest
import io.kritor.group.GetGroupMemberInfoResponse
import io.kritor.group.GetGroupMemberListRequest
import io.kritor.group.GetGroupMemberListResponse
import io.kritor.group.GetNotJoinedGroupInfoRequest
import io.kritor.group.GetNotJoinedGroupInfoResponse
import io.kritor.group.GetProhibitedUserListRequest
import io.kritor.group.GetProhibitedUserListResponse
import io.kritor.group.GetRemainCountAtAllRequest
import io.kritor.group.GetRemainCountAtAllResponse
import io.kritor.group.GroupServiceGrpcKt
import io.kritor.group.KickMemberRequest
import io.kritor.group.KickMemberResponse
import io.kritor.group.LeaveGroupRequest
import io.kritor.group.LeaveGroupResponse
import io.kritor.group.ModifyGroupNameRequest
import io.kritor.group.ModifyGroupNameResponse
import io.kritor.group.ModifyGroupRemarkRequest
import io.kritor.group.ModifyGroupRemarkResponse
import io.kritor.group.ModifyMemberCardRequest
import io.kritor.group.ModifyMemberCardResponse
import io.kritor.group.PokeMemberRequest
import io.kritor.group.PokeMemberResponse
import io.kritor.group.SetGroupAdminRequest
import io.kritor.group.SetGroupAdminResponse
import io.kritor.group.SetGroupUniqueTitleRequest
import io.kritor.group.SetGroupUniqueTitleResponse
import io.kritor.group.SetGroupWholeBanRequest
import io.kritor.group.SetGroupWholeBanResponse
import io.kritor.group.banMemberResponse
import io.kritor.group.getGroupHonorResponse
import io.kritor.group.getGroupInfoResponse
import io.kritor.group.getGroupListResponse
import io.kritor.group.getGroupMemberInfoResponse
import io.kritor.group.getGroupMemberListResponse
import io.kritor.group.getNotJoinedGroupInfoResponse
import io.kritor.group.getProhibitedUserListResponse
import io.kritor.group.getRemainCountAtAllResponse
import io.kritor.group.groupHonorInfo
import io.kritor.group.groupMemberInfo
import io.kritor.group.kickMemberResponse
import io.kritor.group.leaveGroupResponse
import io.kritor.group.modifyGroupNameResponse
import io.kritor.group.modifyGroupRemarkResponse
import io.kritor.group.modifyMemberCardResponse
import io.kritor.group.notJoinedGroupInfo
import io.kritor.group.pokeMemberResponse
import io.kritor.group.prohibitedUserInfo
import io.kritor.group.setGroupAdminResponse
import io.kritor.group.setGroupUniqueTitleResponse
import io.kritor.group.setGroupWholeBanResponse
import io.kritor.group.*
import moe.fuqiuluo.shamrock.helper.TroopHonorHelper.decodeHonor
import moe.fuqiuluo.shamrock.tools.ifNullOrEmpty
import qq.service.contact.ContactHelper
import qq.service.group.GroupHelper
internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase() {
internal object GroupService : GroupServiceGrpcKt.GroupServiceCoroutineImplBase() {
@Grpc("GroupService", "BanMember")
override suspend fun banMember(request: BanMemberRequest): BanMemberResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.banMember(request.groupId, when(request.targetCase!!) {
BanMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
BanMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.duration)
GroupHelper.banMember(
request.groupId, when (request.targetCase!!) {
BanMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
BanMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.duration
)
return banMemberResponse {
return BanMemberResponse.newBuilder().apply {
groupId = request.groupId
}
}.build()
}
@Grpc("GroupService", "PokeMember", )
@Grpc("GroupService", "PokeMember")
override suspend fun pokeMember(request: PokeMemberRequest): PokeMemberResponse {
GroupHelper.pokeMember(request.groupId, when(request.targetCase!!) {
PokeMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
PokeMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
})
return pokeMemberResponse { }
GroupHelper.pokeMember(
request.groupId, when (request.targetCase!!) {
PokeMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
PokeMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}
)
return PokeMemberResponse.newBuilder().build()
}
@Grpc("GroupService", "KickMember")
override suspend fun kickMember(request: KickMemberRequest): KickMemberResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.kickMember(request.groupId, request.rejectAddRequest, if (request.hasKickReason()) request.kickReason else "", when(request.targetCase!!) {
KickMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
KickMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
})
return kickMemberResponse { }
GroupHelper.kickMember(
request.groupId,
request.rejectAddRequest,
if (request.hasKickReason()) request.kickReason else "",
when (request.targetCase!!) {
KickMemberRequest.TargetCase.TARGET_UIN -> request.targetUin
KickMemberRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}
)
return KickMemberResponse.newBuilder().build()
}
@Grpc("GroupService", "LeaveGroup")
override suspend fun leaveGroup(request: LeaveGroupRequest): LeaveGroupResponse {
GroupHelper.resignTroop(request.groupId.toString())
return leaveGroupResponse { }
return LeaveGroupResponse.newBuilder().build()
}
@Grpc("GroupService", "ModifyMemberCard")
override suspend fun modifyMemberCard(request: ModifyMemberCardRequest): ModifyMemberCardResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.modifyGroupMemberCard(request.groupId, when(request.targetCase!!) {
ModifyMemberCardRequest.TargetCase.TARGET_UIN -> request.targetUin
ModifyMemberCardRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.card)
return modifyMemberCardResponse { }
GroupHelper.modifyGroupMemberCard(
request.groupId, when (request.targetCase!!) {
ModifyMemberCardRequest.TargetCase.TARGET_UIN -> request.targetUin
ModifyMemberCardRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid)
.toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.card
)
return ModifyMemberCardResponse.newBuilder().build()
}
@Grpc("GroupService", "ModifyGroupName")
override suspend fun modifyGroupName(request: ModifyGroupNameRequest): ModifyGroupNameResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.modifyTroopName(request.groupId.toString(), request.groupName)
return modifyGroupNameResponse { }
return ModifyGroupNameResponse.newBuilder().build()
}
@Grpc("GroupService", "ModifyGroupRemark")
override suspend fun modifyGroupRemark(request: ModifyGroupRemarkRequest): ModifyGroupRemarkResponse {
GroupHelper.modifyGroupRemark(request.groupId, request.remark)
return modifyGroupRemarkResponse { }
return ModifyGroupRemarkResponse.newBuilder().build()
}
@Grpc("GroupService", "SetGroupAdmin")
override suspend fun setGroupAdmin(request: SetGroupAdminRequest): SetGroupAdminResponse {
if (!GroupHelper.isOwner(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.setGroupAdmin(request.groupId, when(request.targetCase!!) {
SetGroupAdminRequest.TargetCase.TARGET_UIN -> request.targetUin
SetGroupAdminRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.isAdmin)
GroupHelper.setGroupAdmin(
request.groupId, when (request.targetCase!!) {
SetGroupAdminRequest.TargetCase.TARGET_UIN -> request.targetUin
SetGroupAdminRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}, request.isAdmin
)
return setGroupAdminResponse { }
return SetGroupAdminResponse.newBuilder().build()
}
@Grpc("GroupService", "SetGroupUniqueTitle")
override suspend fun setGroupUniqueTitle(request: SetGroupUniqueTitleRequest): SetGroupUniqueTitleResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.setGroupUniqueTitle(request.groupId.toString(), when(request.targetCase!!) {
SetGroupUniqueTitleRequest.TargetCase.TARGET_UIN -> request.targetUin
SetGroupUniqueTitleRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}.toString(), request.uniqueTitle)
GroupHelper.setGroupUniqueTitle(
request.groupId.toString(), when (request.targetCase!!) {
SetGroupUniqueTitleRequest.TargetCase.TARGET_UIN -> request.targetUin
SetGroupUniqueTitleRequest.TargetCase.TARGET_UID -> ContactHelper.getUinByUidAsync(request.targetUid)
.toLong()
return setGroupUniqueTitleResponse { }
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}.toString(), request.uniqueTitle
)
return SetGroupUniqueTitleResponse.newBuilder().build()
}
@Grpc("GroupService", "SetGroupWholeBan")
override suspend fun setGroupWholeBan(request: SetGroupWholeBanRequest): SetGroupWholeBanResponse {
if (!GroupHelper.isAdmin(request.groupId.toString())) {
throw StatusRuntimeException(Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
throw StatusRuntimeException(
Status.PERMISSION_DENIED
.withDescription("You are not admin of this group")
)
}
GroupHelper.setGroupWholeBan(request.groupId, request.isBan)
return setGroupWholeBanResponse { }
return SetGroupWholeBanResponse.newBuilder().build()
}
@Grpc("GroupService", "GetGroupInfo")
@ -215,18 +189,20 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
val groupInfo = GroupHelper.getGroupInfo(request.groupId.toString(), true).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group info").withCause(it))
}.getOrThrow()
return getGroupInfoResponse {
this.groupInfo = io.kritor.group.groupInfo {
return GetGroupInfoResponse.newBuilder().apply {
this.groupInfo = GroupInfo.newBuilder().apply {
groupId = groupInfo.troopcode.toLong()
groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName } ?: ""
groupName =
groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName }
?: ""
groupRemark = groupInfo.troopRemark ?: ""
owner = groupInfo.troopowneruin?.toLong() ?: 0
admins.addAll(GroupHelper.getAdminList(groupId))
addAllAdmins(GroupHelper.getAdminList(groupId))
maxMemberCount = groupInfo.wMemberMax
memberCount = groupInfo.wMemberNum
groupUin = groupInfo.troopuin?.toLong() ?: 0
}
}
}.build()
}.build()
}
@Grpc("GroupService", "GetGroupList")
@ -234,36 +210,45 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
val groupList = GroupHelper.getGroupList(if (request.hasRefresh()) request.refresh else false).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group list").withCause(it))
}.getOrThrow()
return getGroupListResponse {
return GetGroupListResponse.newBuilder().apply {
groupList.forEach { groupInfo ->
this.groupInfo.add(io.kritor.group.groupInfo {
this.addGroupInfo(GroupInfo.newBuilder().apply {
groupId = groupInfo.troopcode.toLong()
groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }.ifNullOrEmpty { groupInfo.newTroopName } ?: ""
groupName = groupInfo.troopname.ifNullOrEmpty { groupInfo.troopRemark }
.ifNullOrEmpty { groupInfo.newTroopName } ?: ""
groupRemark = groupInfo.troopRemark ?: ""
owner = groupInfo.troopowneruin?.toLong() ?: 0
admins.addAll(GroupHelper.getAdminList(groupId))
addAllAdmins(GroupHelper.getAdminList(groupId))
maxMemberCount = groupInfo.wMemberMax
memberCount = groupInfo.wMemberNum
groupUin = groupInfo.troopuin?.toLong() ?: 0
})
}
}
}.build()
}
@Grpc("GroupService", "GetGroupMemberInfo")
override suspend fun getGroupMemberInfo(request: GetGroupMemberInfoRequest): GetGroupMemberInfoResponse {
val memberInfo = GroupHelper.getTroopMemberInfoByUin(request.groupId.toString(), when(request.targetCase!!) {
GetGroupMemberInfoRequest.TargetCase.UIN -> request.uin
GetGroupMemberInfoRequest.TargetCase.UID -> ContactHelper.getUinByUidAsync(request.uid).toLong()
else -> throw StatusRuntimeException(Status.INVALID_ARGUMENT
.withDescription("target not set")
val memberInfo = GroupHelper.getTroopMemberInfoByUin(
request.groupId.toString(), when (request.targetCase!!) {
GetGroupMemberInfoRequest.TargetCase.UIN -> request.uin
GetGroupMemberInfoRequest.TargetCase.UID -> ContactHelper.getUinByUidAsync(request.uid).toLong()
else -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT
.withDescription("target not set")
)
}.toString()
).onFailure {
throw StatusRuntimeException(
Status.INTERNAL.withDescription("unable to get group member info").withCause(it)
)
}.toString()).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member info").withCause(it))
}.getOrThrow()
return getGroupMemberInfoResponse {
groupMemberInfo = groupMemberInfo {
uid = if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.UID) request.uid else ContactHelper.getUidByUinAsync(request.uin)
return GetGroupMemberInfoResponse.newBuilder().apply {
groupMemberInfo = GroupMemberInfo.newBuilder().apply {
uid =
if (request.targetCase == GetGroupMemberInfoRequest.TargetCase.UID) request.uid else ContactHelper.getUidByUinAsync(
request.uin
)
uin = memberInfo.memberuin?.toLong() ?: 0
nick = memberInfo.troopnick
.ifNullOrEmpty { memberInfo.hwName }
@ -279,24 +264,29 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
shutUpTimestamp = memberInfo.gagTimeStamp
distance = memberInfo.distance
honor.addAll((memberInfo.honorList ?: "")
addAllHonor((memberInfo.honorList ?: "")
.split("|")
.filter { it.isNotBlank() }
.map { it.toInt() })
unfriendly = false
cardChangeable = GroupHelper.isAdmin(request.groupId.toString())
}
}
}.build()
}.build()
}
@Grpc("GroupService", "GetGroupMemberList")
override suspend fun getGroupMemberList(request: GetGroupMemberListRequest): GetGroupMemberListResponse {
val memberList = GroupHelper.getGroupMemberList(request.groupId.toString(), if (request.hasRefresh()) request.refresh else false).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member list").withCause(it))
val memberList = GroupHelper.getGroupMemberList(
request.groupId.toString(),
if (request.hasRefresh()) request.refresh else false
).onFailure {
throw StatusRuntimeException(
Status.INTERNAL.withDescription("unable to get group member list").withCause(it)
)
}.getOrThrow()
return getGroupMemberListResponse {
return GetGroupMemberListResponse.newBuilder().apply {
memberList.forEach { memberInfo ->
this.groupMemberInfo.add(groupMemberInfo {
this.addGroupMemberInfo(GroupMemberInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(memberInfo.memberuin?.toLong() ?: 0)
uin = memberInfo.memberuin?.toLong() ?: 0
nick = memberInfo.troopnick
@ -313,7 +303,7 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
shutUpTimestamp = memberInfo.gagTimeStamp
distance = memberInfo.distance
honor.addAll((memberInfo.honorList ?: "")
addAllHonor((memberInfo.honorList ?: "")
.split("|")
.filter { it.isNotBlank() }
.map { it.toInt() })
@ -321,23 +311,25 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
cardChangeable = GroupHelper.isAdmin(request.groupId.toString())
})
}
}
}.build()
}
@Grpc("GroupService", "GetProhibitedUserList")
override suspend fun getProhibitedUserList(request: GetProhibitedUserListRequest): GetProhibitedUserListResponse {
val prohibitedList = GroupHelper.getProhibitedMemberList(request.groupId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get prohibited user list").withCause(it))
throw StatusRuntimeException(
Status.INTERNAL.withDescription("unable to get prohibited user list").withCause(it)
)
}.getOrThrow()
return getProhibitedUserListResponse {
return GetProhibitedUserListResponse.newBuilder().apply {
prohibitedList.forEach {
this.prohibitedUserInfo.add(prohibitedUserInfo {
this.addProhibitedUserInfo(ProhibitedUserInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(it.memberUin)
uin = it.memberUin
prohibitedTime = it.shutuptimestap
})
}
}
}.build()
}
@Grpc("GroupService", "GetRemainCountAtAll")
@ -345,20 +337,22 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
val remainAtAllRsp = GroupHelper.getGroupRemainAtAllRemain(request.groupId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get remain count").withCause(it))
}.getOrThrow()
return getRemainCountAtAllResponse {
return GetRemainCountAtAllResponse.newBuilder().apply {
accessAtAll = remainAtAllRsp.bool_can_at_all.get()
remainCountForGroup = remainAtAllRsp.uint32_remain_at_all_count_for_group.get()
remainCountForSelf = remainAtAllRsp.uint32_remain_at_all_count_for_uin.get()
}
}.build()
}
@Grpc("GroupService", "GetNotJoinedGroupInfo")
override suspend fun getNotJoinedGroupInfo(request: GetNotJoinedGroupInfoRequest): GetNotJoinedGroupInfoResponse {
val groupInfo = GroupHelper.getNotJoinedGroupInfo(request.groupId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get not joined group info").withCause(it))
throw StatusRuntimeException(
Status.INTERNAL.withDescription("unable to get not joined group info").withCause(it)
)
}.getOrThrow()
return getNotJoinedGroupInfoResponse {
this.groupInfo = notJoinedGroupInfo {
return GetNotJoinedGroupInfoResponse.newBuilder().apply {
this.groupInfo = NotJoinedGroupInfo.newBuilder().apply {
groupId = groupInfo.groupId
groupName = groupInfo.groupName
owner = groupInfo.owner
@ -368,15 +362,17 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
createTime = groupInfo.createTime.toInt()
groupFlag = groupInfo.groupFlag
groupFlagExt = groupInfo.groupFlagExt
}
}
}.build()
}.build()
}
@Grpc("GroupService", "GetGroupHonor")
override suspend fun getGroupHonor(request: GetGroupHonorRequest): GetGroupHonorResponse {
return getGroupHonorResponse {
return GetGroupHonorResponse.newBuilder().apply {
GroupHelper.getGroupMemberList(request.groupId.toString(), true).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get group member list").withCause(it))
throw StatusRuntimeException(
Status.INTERNAL.withDescription("unable to get group member list").withCause(it)
)
}.onSuccess { memberList ->
memberList.forEach { member ->
(member.honorList ?: "").split("|")
@ -384,7 +380,7 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
.map { it.toInt() }.forEach {
val honor = decodeHonor(member.memberuin.toLong(), it, member.mHonorRichFlag)
if (honor != null) {
groupHonorInfo.add(groupHonorInfo {
addGroupHonorInfo(GroupHonorInfo.newBuilder().apply {
uid = ContactHelper.getUidByUinAsync(member.memberuin.toLong())
uin = member.memberuin.toLong()
nick = member.troopnick
@ -400,6 +396,6 @@ internal object GroupService: GroupServiceGrpcKt.GroupServiceCoroutineImplBase()
}
}
}
}
}.build()
}
}

View File

@ -4,25 +4,7 @@ import android.util.Base64
import com.tencent.mobileqq.app.QQAppInterface
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.core.ClearCacheRequest
import io.kritor.core.ClearCacheResponse
import io.kritor.core.DownloadFileRequest
import io.kritor.core.DownloadFileResponse
import io.kritor.core.GetCurrentAccountRequest
import io.kritor.core.GetCurrentAccountResponse
import io.kritor.core.GetDeviceBatteryRequest
import io.kritor.core.GetDeviceBatteryResponse
import io.kritor.core.GetVersionRequest
import io.kritor.core.GetVersionResponse
import io.kritor.core.KritorServiceGrpcKt
import io.kritor.core.SwitchAccountRequest
import io.kritor.core.SwitchAccountResponse
import io.kritor.core.clearCacheResponse
import io.kritor.core.downloadFileResponse
import io.kritor.core.getCurrentAccountResponse
import io.kritor.core.getDeviceBatteryResponse
import io.kritor.core.getVersionResponse
import io.kritor.core.switchAccountResponse
import io.kritor.core.*
import moe.fuqiuluo.shamrock.tools.ShamrockVersion
import moe.fuqiuluo.shamrock.utils.DownloadUtils
import moe.fuqiuluo.shamrock.utils.FileUtils
@ -30,18 +12,17 @@ import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.utils.MMKVFetcher
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import mqq.app.MobileQQ
import qq.service.QQInterfaces
import qq.service.QQInterfaces.Companion.app
import qq.service.contact.ContactHelper
import java.io.File
internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBase() {
internal object KritorService : KritorServiceGrpcKt.KritorServiceCoroutineImplBase() {
@Grpc("KritorService", "GetVersion")
override suspend fun getVersion(request: GetVersionRequest): GetVersionResponse {
return getVersionResponse {
return GetVersionResponse.newBuilder().apply {
this.version = ShamrockVersion
this.appName = "Shamrock"
}
}.build()
}
@Grpc("KritorService", "ClearCache")
@ -49,16 +30,16 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas
FileUtils.clearCache()
MMKVFetcher.mmkvWithId("audio2silk")
.clear()
return clearCacheResponse {}
return ClearCacheResponse.newBuilder().build()
}
@Grpc("KritorService", "GetCurrentAccount")
override suspend fun getCurrentAccount(request: GetCurrentAccountRequest): GetCurrentAccountResponse {
return getCurrentAccountResponse {
return GetCurrentAccountResponse.newBuilder().apply {
this.accountName = if (app is QQAppInterface) app.currentNickname else "unknown"
this.accountUid = app.currentUid ?: ""
this.accountUin = (app.currentUin ?: "0").toLong()
}
}.build()
}
@Grpc("KritorService", "DownloadFile")
@ -80,13 +61,14 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas
if (request.hasBase64()) {
val bytes = Base64.decode(request.base64, Base64.DEFAULT)
tmp.writeBytes(bytes)
} else if(request.hasUrl()) {
if(!DownloadUtils.download(
} else if (request.hasUrl()) {
if (!DownloadUtils.download(
urlAdr = request.url,
dest = tmp,
headers = headerMap,
threadCount = if (request.hasThreadCnt()) request.threadCnt else 3
)) {
)
) {
throw StatusRuntimeException(Status.INTERNAL.withDescription("download failed"))
}
}
@ -100,18 +82,22 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas
}
}
return downloadFileResponse {
return DownloadFileResponse.newBuilder().apply {
this.fileMd5 = MD5.genFileMd5Hex(tmp.absolutePath)
this.fileAbsolutePath = tmp.absolutePath
}
}.build()
}
@Grpc("KritorService", "SwitchAccount")
override suspend fun switchAccount(request: SwitchAccountRequest): SwitchAccountResponse {
val uin = when(request.accountCase!!) {
val uin = when (request.accountCase!!) {
SwitchAccountRequest.AccountCase.ACCOUNT_UID -> ContactHelper.getUinByUidAsync(request.accountUid)
SwitchAccountRequest.AccountCase.ACCOUNT_UIN -> request.accountUin.toString()
SwitchAccountRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("account not found"))
SwitchAccountRequest.AccountCase.ACCOUNT_NOT_SET -> throw StatusRuntimeException(
Status.INVALID_ARGUMENT.withDescription(
"account not found"
)
)
}
val account = MobileQQ.getMobileQQ().allAccounts.firstOrNull { it.uin == uin }
?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("account not found"))
@ -120,17 +106,17 @@ internal object KritorService: KritorServiceGrpcKt.KritorServiceCoroutineImplBas
}.onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it).withDescription("failed to switch account"))
}
return switchAccountResponse { }
return SwitchAccountResponse.newBuilder().build()
}
@Grpc("KritorService", "GetDeviceBattery")
override suspend fun getDeviceBattery(request: GetDeviceBatteryRequest): GetDeviceBatteryResponse {
return getDeviceBatteryResponse {
return GetDeviceBatteryResponse.newBuilder().apply {
PlatformUtils.getDeviceBattery().let {
this.battery = it.battery
this.scale = it.scale
this.status = it.status
}
}
}.build()
}
}

View File

@ -7,48 +7,8 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.message.ClearMessagesRequest
import io.kritor.message.ClearMessagesResponse
import io.kritor.message.DeleteEssenceMsgRequest
import io.kritor.message.DeleteEssenceMsgResponse
import io.kritor.message.GetEssenceMessagesRequest
import io.kritor.message.GetEssenceMessagesResponse
import io.kritor.message.GetForwardMessagesRequest
import io.kritor.message.GetForwardMessagesResponse
import io.kritor.message.GetHistoryMessageRequest
import io.kritor.message.GetHistoryMessageResponse
import io.kritor.message.GetMessageBySeqRequest
import io.kritor.message.GetMessageBySeqResponse
import io.kritor.message.GetMessageRequest
import io.kritor.message.GetMessageResponse
import io.kritor.message.MessageServiceGrpcKt
import io.kritor.message.RecallMessageRequest
import io.kritor.message.RecallMessageResponse
import io.kritor.message.Scene
import io.kritor.message.SendMessageByResIdRequest
import io.kritor.message.SendMessageByResIdResponse
import io.kritor.message.SendMessageRequest
import io.kritor.message.SendMessageResponse
import io.kritor.message.SetEssenceMessageRequest
import io.kritor.message.SetEssenceMessageResponse
import io.kritor.message.SetMessageCommentEmojiRequest
import io.kritor.message.SetMessageCommentEmojiResponse
import io.kritor.message.clearMessagesResponse
import io.kritor.message.contact
import io.kritor.message.deleteEssenceMsgResponse
import io.kritor.message.essenceMessage
import io.kritor.message.getEssenceMessagesResponse
import io.kritor.message.getForwardMessagesResponse
import io.kritor.message.getHistoryMessageResponse
import io.kritor.message.getMessageBySeqResponse
import io.kritor.message.getMessageResponse
import io.kritor.message.messageBody
import io.kritor.message.recallMessageResponse
import io.kritor.message.sendMessageByResIdResponse
import io.kritor.message.sendMessageResponse
import io.kritor.message.sender
import io.kritor.message.setEssenceMessageResponse
import io.kritor.message.setMessageCommentEmojiResponse
import io.kritor.event.MessageEvent
import io.kritor.message.*
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.Level
@ -75,27 +35,34 @@ import kotlin.coroutines.resume
import kotlin.random.Random
import kotlin.random.nextUInt
internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImplBase() {
internal object MessageService : MessageServiceGrpcKt.MessageServiceCoroutineImplBase() {
@Grpc("MessageService", "SendMessage")
override suspend fun sendMessage(request: SendMessageRequest): SendMessageResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val uniseq = MessageHelper.generateMsgId(contact.chatType)
return sendMessageResponse {
this.messageId = MessageHelper.sendMessage(contact, NtMsgConvertor.convertToNtMsgs(contact, uniseq, request.elementsList), request.retryCount, uniseq).onFailure {
return SendMessageResponse.newBuilder().apply {
this.messageId = MessageHelper.sendMessage(
contact,
NtMsgConvertor.convertToNtMsgs(contact, uniseq, request.elementsList),
request.retryCount,
uniseq
).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow()
}
}.build()
}
@Grpc("MessageService", "SendMessageByResId")
@ -125,7 +92,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
msgVia = 0u
)
QQInterfaces.sendBuffer("MessageSvc.PbSendMsg", true, req.toByteArray())
return sendMessageByResIdResponse { }
return SendMessageByResIdResponse.newBuilder().build()
}
@Grpc("MessageService", "ClearMessages")
@ -134,7 +101,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
val service = sessionService.msgService
val chatType = when(contact.scene!!) {
val chatType = when (contact.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
@ -144,21 +111,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}
service.clearMsgRecords(Contact(chatType, contact.peer, contact.subPeer), null)
return clearMessagesResponse { }
return ClearMessagesResponse.newBuilder().build()
}
@Grpc("MessageService", "RecallMessage")
override suspend fun recallMessage(request: RecallMessageRequest): RecallMessageResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val kernelService = NTServiceFetcher.kernelService
val sessionService = kernelService.wrapperSession
@ -169,62 +138,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
}
return recallMessageResponse {}
}
@Grpc("MessageService", "GetForwardMessages")
override suspend fun getForwardMessages(request: GetForwardMessagesRequest): GetForwardMessagesResponse {
return getForwardMessagesResponse {
MessageHelper.getForwardMsg(request.resId).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow().forEach { detail ->
messages.add(messageBody {
val peer = when (scene) {
Scene.GROUP -> detail.groupId.toString()
Scene.FRIEND -> detail.sender.userId.toString()
else -> detail.peerId.toString()
}
this.time = detail.time
this.scene = when(detail.msgType) {
MsgConstant.KCHATTYPEC2C -> Scene.FRIEND
MsgConstant.KCHATTYPEGROUP -> Scene.GROUP
MsgConstant.KCHATTYPEGUILD -> Scene.GUILD
MsgConstant.KCHATTYPETEMPC2CFROMGROUP -> Scene.STRANGER_FROM_GROUP
MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN -> Scene.NEARBY
else -> Scene.STRANGER
}
this.messageId = detail.qqMsgId
this.messageSeq = detail.msgSeq
this.contact = contact {
this.scene = scene
this.peer = peer
}
this.sender = sender {
this.uin = detail.sender.userId
this.nick = detail.sender.nickName
this.uid = detail.sender.uid
}
detail.message?.elements?.toKritorResponseMessages(Contact(detail.msgType, peer, null))?.let {
this.elements.addAll(it)
}
})
}
}
return RecallMessageResponse.newBuilder().build()
}
@Grpc("MessageService", "GetMessage")
override suspend fun getMessage(request: GetMessageRequest): GetMessageResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
@ -242,34 +172,35 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
return getMessageResponse {
this.message = messageBody {
return GetMessageResponse.newBuilder().apply {
this.message = MessageEvent.newBuilder().apply {
this.messageId = msg.msgId
this.scene = request.contact.scene
this.contact = request.contact
this.sender = sender {
this.sender = Sender.newBuilder().apply {
this.uin = msg.senderUin
this.nick = msg.sendNickName ?: ""
this.uid = msg.senderUid ?: ""
}
}.build()
this.messageSeq = msg.msgSeq
this.elements.addAll(msg.elements.toKritorReqMessages(contact))
}
}
this.addAllElements(msg.elements.toKritorReqMessages(contact))
}.build()
}.build()
}
@Grpc("MessageService", "GetMessageBySeq")
override suspend fun getMessageBySeq(request: GetMessageBySeqRequest): GetMessageBySeqResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
@ -287,34 +218,35 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
return getMessageBySeqResponse {
this.message = messageBody {
return GetMessageBySeqResponse.newBuilder().apply {
this.message = MessageEvent.newBuilder().apply {
this.messageId = msg.msgId
this.scene = request.contact.scene
this.contact = request.contact
this.sender = sender {
this.sender = Sender.newBuilder().apply {
this.uin = msg.senderUin
this.nick = msg.sendNickName ?: ""
this.uid = msg.senderUid ?: ""
}
}.build()
this.messageSeq = msg.msgSeq
this.elements.addAll(msg.elements.toKritorReqMessages(contact))
}
}
this.addAllElements(msg.elements.toKritorReqMessages(contact))
}.build()
}.build()
}
@Grpc("MessageService", "GetHistoryMessage")
override suspend fun getHistoryMessage(request: GetHistoryMessageRequest): GetHistoryMessageResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val msgs: List<MsgRecord> = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
@ -332,22 +264,21 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Messages not found"))
return getHistoryMessageResponse {
return GetHistoryMessageResponse.newBuilder().apply {
msgs.forEach {
messages.add(messageBody {
addMessages(MessageEvent.newBuilder().apply {
this.messageId = it.msgId
this.scene = request.contact.scene
this.contact = request.contact
this.sender = sender {
this.sender = Sender.newBuilder().apply {
this.uin = it.senderUin
this.nick = it.sendNickName ?: ""
this.uid = it.senderUid ?: ""
}
}.build()
this.messageSeq = it.msgSeq
this.elements.addAll(it.elements.toKritorReqMessages(contact))
this.addAllElements(it.elements.toKritorReqMessages(contact))
})
}
}
}.build()
}
@Grpc("MessageService", "DeleteEssenceMsg")
@ -368,19 +299,19 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
}
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
if(MessageHelper.deleteEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null)
if (MessageHelper.deleteEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null)
throw StatusRuntimeException(Status.NOT_FOUND.withDescription("delete essence message failed"))
return deleteEssenceMsgResponse { }
return DeleteEssenceMsgResponse.newBuilder().build()
}
@Grpc("MessageService", "GetEssenceMessages")
override suspend fun getEssenceMessages(request: GetEssenceMessagesRequest): GetEssenceMessagesResponse {
val contact = MessageHelper.generateContact(MsgConstant.KCHATTYPEGROUP, request.groupId.toString())
return getEssenceMessagesResponse {
return GetEssenceMessagesResponse.newBuilder().apply {
MessageHelper.getEssenceMessageList(request.groupId, request.page, request.pageSize).onFailure {
throw StatusRuntimeException(Status.INTERNAL.withCause(it))
}.getOrThrow().forEach {
essenceMessage.add(essenceMessage {
addEssenceMessage(EssenceMessage.newBuilder().apply {
withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation ->
@ -408,7 +339,7 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
this.jsonElements = it.messageContent.toString()
})
}
}
}.build()
}
@Grpc("MessageService", "SetEssenceMessage")
@ -432,21 +363,23 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
if (MessageHelper.setEssenceMessage(request.groupId, msg.msgSeq, msg.msgRandom) == null) {
throw StatusRuntimeException(Status.NOT_FOUND.withDescription("set essence message failed"))
}
return setEssenceMessageResponse { }
return SetEssenceMessageResponse.newBuilder().build()
}
@Grpc("MessageService", "SetMessageCommentEmoji")
override suspend fun setMessageCommentEmoji(request: SetMessageCommentEmojiRequest): SetMessageCommentEmojiResponse {
val contact = request.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val msg: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
@ -463,7 +396,12 @@ internal object MessageService: MessageServiceGrpcKt.MessageServiceCoroutineImpl
}
}
} ?: throw StatusRuntimeException(Status.NOT_FOUND.withDescription("Message not found"))
MessageHelper.setGroupMessageCommentFace(request.contact.longPeer(), msg.msgSeq.toULong(), request.faceId.toString(), request.isComment)
return setMessageCommentEmojiResponse { }
MessageHelper.setGroupMessageCommentFace(
request.contact.longPeer(),
msg.msgSeq.toULong(),
request.faceId.toString(),
request.isComment
)
return SetMessageCommentEmojiResponse.newBuilder().build()
}
}

View File

@ -2,36 +2,24 @@ package kritor.service
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.web.GetCSRFTokenRequest
import io.kritor.web.GetCSRFTokenResponse
import io.kritor.web.GetCookiesRequest
import io.kritor.web.GetCookiesResponse
import io.kritor.web.GetCredentialsRequest
import io.kritor.web.GetCredentialsResponse
import io.kritor.web.GetHttpCookiesRequest
import io.kritor.web.GetHttpCookiesResponse
import io.kritor.web.WebServiceGrpcKt
import io.kritor.web.getCSRFTokenResponse
import io.kritor.web.getCookiesResponse
import io.kritor.web.getCredentialsResponse
import io.kritor.web.getHttpCookiesResponse
import io.kritor.web.*
import qq.service.ticket.TicketHelper
internal object WebService: WebServiceGrpcKt.WebServiceCoroutineImplBase() {
@Grpc("WebService", "GetCookies")
override suspend fun getCookies(request: GetCookiesRequest): GetCookiesResponse {
return getCookiesResponse {
return GetCookiesResponse.newBuilder().apply {
if (request.domain.isNullOrEmpty()) {
this.cookie = TicketHelper.getCookie()
} else {
this.cookie = TicketHelper.getCookie(request.domain)
}
}
}.build()
}
@Grpc("WebService", "GetCredentials")
override suspend fun getCredentials(request: GetCredentialsRequest): GetCredentialsResponse {
return getCredentialsResponse {
return GetCredentialsResponse.newBuilder().apply {
if (request.domain.isNullOrEmpty()) {
val uin = TicketHelper.getUin()
val skey = TicketHelper.getRealSkey(uin)
@ -46,27 +34,25 @@ internal object WebService: WebServiceGrpcKt.WebServiceCoroutineImplBase() {
this.cookie = "o_cookie=$uin; ied_qq=o$uin; pac_uid=1_$uin; uin=o$uin; skey=$skey; p_uin=o$uin; p_skey=$pskey; pt4_token=$pt4token;"
this.bkn = TicketHelper.getCSRF(pskey)
}
}
}.build()
}
@Grpc("WebService", "GetCSRFToken")
override suspend fun getCSRFToken(request: GetCSRFTokenRequest): GetCSRFTokenResponse {
return getCSRFTokenResponse {
return GetCSRFTokenResponse.newBuilder().apply {
if (request.domain.isNullOrEmpty()) {
this.bkn = TicketHelper.getCSRF()
} else {
this.bkn = TicketHelper.getCSRF(TicketHelper.getUin(), request.domain)
}
}
}.build()
}
@Grpc("WebService", "GetHttpCookies")
override suspend fun getHttpCookies(request: GetHttpCookiesRequest): GetHttpCookiesResponse {
return getHttpCookiesResponse {
return GetHttpCookiesResponse.newBuilder().apply {
this.cookie = TicketHelper.getHttpCookies(request.appid, request.daid, request.jumpUrl)
?: throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to get http cookies"))
}
}.build()
}
}

View File

@ -4,37 +4,9 @@ package moe.fuqiuluo.shamrock.internals
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import io.kritor.event.GroupApplyType
import io.kritor.event.GroupMemberBanType
import io.kritor.event.GroupMemberDecreasedType
import io.kritor.event.GroupMemberIncreasedType
import io.kritor.event.MessageEvent
import io.kritor.event.NoticeEvent
import io.kritor.event.NoticeType
import io.kritor.event.RequestType
import io.kritor.event.RequestsEvent
import io.kritor.event.Scene
import io.kritor.event.contact
import io.kritor.event.essenceMessageNotice
import io.kritor.event.friendApplyRequest
import io.kritor.event.friendFileComeNotice
import io.kritor.event.friendPokeNotice
import io.kritor.event.friendRecallNotice
import io.kritor.event.groupAdminChangedNotice
import io.kritor.event.groupApplyRequest
import io.kritor.event.groupFileComeNotice
import io.kritor.event.groupMemberBannedNotice
import io.kritor.event.groupMemberDecreasedNotice
import io.kritor.event.groupMemberIncreasedNotice
import io.kritor.event.groupPokeNotice
import io.kritor.event.groupRecallNotice
import io.kritor.event.groupSignNotice
import io.kritor.event.groupUniqueTitleChangedNotice
import io.kritor.event.groupWholeBanNotice
import io.kritor.event.messageEvent
import io.kritor.event.noticeEvent
import io.kritor.event.requestsEvent
import io.kritor.event.sender
import io.kritor.event.*
import io.kritor.message.Contact
import io.kritor.message.Sender
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.FlowCollector
@ -43,8 +15,8 @@ import kotlinx.coroutines.launch
import qq.service.QQInterfaces
import qq.service.msg.toKritorEventMessages
internal object GlobalEventTransmitter: QQInterfaces() {
private val messageEventFlow by lazy {
internal object GlobalEventTransmitter : QQInterfaces() {
private val MessageEventFlow by lazy {
MutableSharedFlow<Pair<MsgRecord, MessageEvent>>()
}
private val noticeEventFlow by lazy {
@ -58,30 +30,30 @@ internal object GlobalEventTransmitter: QQInterfaces() {
private suspend fun pushRequest(requestEvent: RequestsEvent) = requestEventFlow.emit(requestEvent)
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) = messageEventFlow.emit(record to message)
private suspend fun transMessageEvent(record: MsgRecord, message: MessageEvent) =
MessageEventFlow.emit(record to message)
object MessageTransmitter {
suspend fun transGroupMessage(
record: MsgRecord,
elements: ArrayList<MsgElement>,
): Boolean {
transMessageEvent(record, messageEvent {
transMessageEvent(record, MessageEvent.newBuilder().apply {
this.time = record.msgTime.toInt()
this.scene = Scene.GROUP
this.messageId = record.msgId
this.messageSeq = record.msgSeq
this.contact = contact {
this.contact = Contact.newBuilder().apply {
this.scene = scene
this.peer = record.peerUin.toString()
this.subPeer = record.peerUid
}
this.sender = sender {
}.build()
this.sender = Sender.newBuilder().apply {
this.uin = record.senderUin
this.uid = record.senderUid
this.nick = record.sendNickName
}
this.elements.addAll(elements.toKritorEventMessages(record))
})
}.build()
this.addAllElements(elements.toKritorEventMessages(record))
}.build())
return true
}
@ -89,23 +61,22 @@ internal object GlobalEventTransmitter: QQInterfaces() {
record: MsgRecord,
elements: ArrayList<MsgElement>,
): Boolean {
transMessageEvent(record, messageEvent {
transMessageEvent(record, MessageEvent.newBuilder().apply {
this.time = record.msgTime.toInt()
this.scene = Scene.FRIEND
this.messageId = record.msgId
this.messageSeq = record.msgSeq
this.contact = contact {
this.contact = Contact.newBuilder().apply {
this.scene = scene
this.peer = record.senderUin.toString()
this.subPeer = record.senderUid
}
this.sender = sender {
}.build()
this.sender = Sender.newBuilder().apply {
this.uin = record.senderUin
this.uid = record.senderUid
this.nick = record.sendNickName
}
this.elements.addAll(elements.toKritorEventMessages(record))
})
}.build()
this.addAllElements(elements.toKritorEventMessages(record))
}.build())
return true
}
@ -115,23 +86,22 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long,
fromNick: String,
): Boolean {
transMessageEvent(record, messageEvent {
transMessageEvent(record, MessageEvent.newBuilder().apply {
this.time = record.msgTime.toInt()
this.scene = Scene.FRIEND
this.messageId = record.msgId
this.messageSeq = record.msgSeq
this.contact = contact {
this.contact = Contact.newBuilder().apply {
this.scene = scene
this.peer = record.senderUin.toString()
this.subPeer = groupCode.toString()
}
this.sender = sender {
}.build()
this.sender = Sender.newBuilder().apply {
this.uin = record.senderUin
this.uid = record.senderUid
this.nick = record.sendNickName
}
this.elements.addAll(elements.toKritorEventMessages(record))
})
}.build()
this.addAllElements(elements.toKritorEventMessages(record))
}.build())
return true
}
@ -139,23 +109,22 @@ internal object GlobalEventTransmitter: QQInterfaces() {
record: MsgRecord,
elements: ArrayList<MsgElement>,
): Boolean {
transMessageEvent(record, messageEvent {
transMessageEvent(record, MessageEvent.newBuilder().apply {
this.time = record.msgTime.toInt()
this.scene = Scene.GUILD
this.messageId = record.msgId
this.messageSeq = record.msgSeq
this.contact = contact {
this.contact = Contact.newBuilder().apply {
this.scene = scene
this.peer = record.guildId ?: ""
this.subPeer = record.channelId ?: ""
}
this.sender = sender {
}.build()
this.sender = Sender.newBuilder().apply {
this.uin = record.senderUin
this.uid = record.senderUid
this.nick = record.sendNickName
}
this.elements.addAll(elements.toKritorEventMessages(record))
})
}.build()
this.addAllElements(elements.toKritorEventMessages(record))
}.build())
return true
}
}
@ -177,10 +146,10 @@ internal object GlobalEventTransmitter: QQInterfaces() {
expireTime: Long,
url: String
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_FILE_COME
this.time = msgTime.toInt()
this.friendFileCome = friendFileComeNotice {
this.friendFileCome = FriendFileComeNotice.newBuilder().apply {
this.fileId = fileId
this.fileName = fileName
this.operator = userId
@ -188,8 +157,8 @@ internal object GlobalEventTransmitter: QQInterfaces() {
this.expireTime = expireTime.toInt()
this.fileSubId = fileSubId
this.url = url
}
})
}.build()
}.build())
return true
}
@ -206,10 +175,10 @@ internal object GlobalEventTransmitter: QQInterfaces() {
bizId: Int,
url: String
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_FILE_COME
this.time = msgTime.toInt()
this.groupFileCome = groupFileComeNotice {
this.groupFileCome = GroupFileComeNotice.newBuilder().apply {
this.groupId = groupId
this.operator = userId
this.fileId = uuid
@ -217,8 +186,8 @@ internal object GlobalEventTransmitter: QQInterfaces() {
this.fileSize = fileSize
this.biz = bizId
this.url = url
}
})
}.build()
}.build())
return true
}
}
@ -227,33 +196,47 @@ internal object GlobalEventTransmitter: QQInterfaces() {
* 群聊通知 通知器
*/
object GroupNoticeTransmitter {
suspend fun transGroupSign(time: Long, target: Long, action: String?, rankImg: String?, groupCode: Long): Boolean {
pushNotice(noticeEvent {
suspend fun transGroupSign(
time: Long,
target: Long,
action: String?,
rankImg: String?,
groupCode: Long
): Boolean {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_SIGN
this.time = time.toInt()
this.groupSign = groupSignNotice {
this.groupSign = GroupSignNotice.newBuilder().apply {
this.groupId = groupCode
this.targetUin = target
this.action = action ?: ""
this.suffix = ""
this.rankImage = rankImg ?: ""
}
})
}.build()
}.build())
return true
}
suspend fun transGroupPoke(time: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?, groupCode: Long): Boolean {
pushNotice(noticeEvent {
suspend fun transGroupPoke(
time: Long,
operator: Long,
target: Long,
action: String?,
suffix: String?,
actionImg: String?,
groupCode: Long
): Boolean {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_POKE
this.time = time.toInt()
this.groupPoke = groupPokeNotice {
this.groupPoke = GroupPokeNotice.newBuilder().apply {
this.action = action ?: ""
this.target = target
this.operator = operator
this.suffix = suffix ?: ""
this.actionImage = actionImg ?: ""
}
})
}.build()
}.build())
return true
}
@ -266,18 +249,18 @@ internal object GlobalEventTransmitter: QQInterfaces() {
operatorUid: String,
type: GroupMemberIncreasedType
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_INCREASE
this.time = time.toInt()
this.groupMemberIncrease = groupMemberIncreasedNotice {
this.groupMemberIncrease = GroupMemberIncreasedNotice.newBuilder().apply {
this.groupId = groupCode
this.operatorUid = operatorUid
this.operatorUin = operator
this.targetUid = targetUid
this.targetUin = target
this.type = type
}
})
}.build()
}.build())
return true
}
@ -290,18 +273,18 @@ internal object GlobalEventTransmitter: QQInterfaces() {
operatorUid: String,
type: GroupMemberDecreasedType
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_INCREASE
this.time = time.toInt()
this.groupMemberDecrease = groupMemberDecreasedNotice {
this.groupMemberDecrease = GroupMemberDecreasedNotice.newBuilder().apply {
this.groupId = groupCode
this.operatorUid = operatorUid
this.operatorUin = operator
this.targetUid = targetUid
this.targetUin = target
this.type = type
}
})
}.build()
}.build())
return true
}
@ -312,16 +295,16 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long,
setAdmin: Boolean
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_ADMIN_CHANGED
this.time = msgTime.toInt()
this.groupAdminChanged = groupAdminChangedNotice {
this.groupAdminChanged = GroupAdminChangedNotice.newBuilder().apply {
this.groupId = groupCode
this.targetUid = targetUid
this.targetUin = target
this.isAdmin = setAdmin
}
})
}.build()
}.build())
return true
}
@ -331,15 +314,15 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long,
isOpen: Boolean
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_WHOLE_BAN
this.time = msgTime.toInt()
this.groupWholeBan = groupWholeBanNotice {
this.groupWholeBan = GroupWholeBanNotice.newBuilder().apply {
this.groupId = groupCode
this.isWholeBan = isOpen
this.operator = operator
}
})
}.build()
}.build())
return true
}
@ -352,10 +335,10 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupCode: Long,
duration: Int
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_BANNED
this.time = msgTime.toInt()
this.groupMemberBanned = groupMemberBannedNotice {
this.groupMemberBanned = GroupMemberBannedNotice.newBuilder().apply {
this.groupId = groupCode
this.operatorUid = operatorUid
this.operatorUin = operator
@ -364,8 +347,8 @@ internal object GlobalEventTransmitter: QQInterfaces() {
this.duration = duration
this.type = if (duration > 0) GroupMemberBanType.BAN
else GroupMemberBanType.LIFT_BAN
}
})
}.build()
}.build())
return true
}
@ -379,10 +362,10 @@ internal object GlobalEventTransmitter: QQInterfaces() {
msgId: Long,
tipText: String
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_RECALL
this.time = time.toInt()
this.groupRecall = groupRecallNotice {
this.groupRecall = GroupRecallNotice.newBuilder().apply {
this.groupId = groupCode
this.operatorUid = operatorUid
this.operatorUin = operator
@ -390,8 +373,8 @@ internal object GlobalEventTransmitter: QQInterfaces() {
this.targetUin = target
this.messageId = msgId
this.tipText = tipText
}
})
}.build()
}.build())
return true
}
@ -412,15 +395,15 @@ internal object GlobalEventTransmitter: QQInterfaces() {
title: String,
groupId: Long
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_MEMBER_UNIQUE_TITLE_CHANGED
this.time = time.toInt()
this.groupMemberUniqueTitleChanged = groupUniqueTitleChangedNotice {
this.groupMemberUniqueTitleChanged = GroupUniqueTitleChangedNotice.newBuilder().apply {
this.groupId = groupId
this.target = targetId
this.title = title
}
})
}.build()
}.build())
return true
}
@ -432,17 +415,17 @@ internal object GlobalEventTransmitter: QQInterfaces() {
groupId: Long,
subType: UInt
): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.GROUP_ESSENCE_CHANGED
this.time = time.toInt()
this.groupEssenceChanged = essenceMessageNotice {
this.groupEssenceChanged = EssenceMessageNotice.newBuilder().apply {
this.groupId = groupId
this.messageId = msgId
this.sender = senderUin
this.operator = operatorUin
this.subType = subType.toInt()
}
})
}.build()
}.build())
return true
}
}
@ -451,31 +434,38 @@ internal object GlobalEventTransmitter: QQInterfaces() {
* 私聊通知 通知器
*/
object PrivateNoticeTransmitter {
suspend fun transPrivatePoke(msgTime: Long, operator: Long, target: Long, action: String?, suffix: String?, actionImg: String?): Boolean {
pushNotice(noticeEvent {
suspend fun transPrivatePoke(
msgTime: Long,
operator: Long,
target: Long,
action: String?,
suffix: String?,
actionImg: String?
): Boolean {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_POKE
this.time = msgTime.toInt()
this.friendPoke = friendPokeNotice {
this.friendPoke = FriendPokeNotice.newBuilder().apply {
this.action = action ?: ""
this.target = target
this.operator = operator
this.suffix = suffix ?: ""
this.actionImage = actionImg ?: ""
}
})
}.build()
}.build())
return true
}
suspend fun transPrivateRecall(time: Long, operator: Long, msgId: Long, tipText: String): Boolean {
pushNotice(noticeEvent {
pushNotice(NoticeEvent.newBuilder().apply {
this.type = NoticeType.FRIEND_RECALL
this.time = time.toInt()
this.friendRecall = friendRecallNotice {
this.friendRecall = FriendRecallNotice.newBuilder().apply {
this.operator = operator
this.messageId = msgId
this.tipText = tipText
}
})
}.build()
}.build())
return true
}
@ -486,15 +476,15 @@ internal object GlobalEventTransmitter: QQInterfaces() {
*/
object RequestTransmitter {
suspend fun transFriendApp(time: Long, operator: Long, tipText: String, flag: String): Boolean {
pushRequest(requestsEvent {
pushRequest(RequestsEvent.newBuilder().apply {
this.type = RequestType.FRIEND_APPLY
this.time = time.toInt()
this.friendApply = friendApplyRequest {
this.friendApply = FriendApplyRequest.newBuilder().apply {
this.applierUin = operator
this.message = tipText
this.flag = flag
}
})
}.build()
}.build())
return true
}
@ -507,24 +497,24 @@ internal object GlobalEventTransmitter: QQInterfaces() {
flag: String,
type: GroupApplyType
): Boolean {
pushRequest(requestsEvent {
pushRequest(RequestsEvent.newBuilder().apply {
this.type = RequestType.GROUP_APPLY
this.time = time.toInt()
this.groupApply = groupApplyRequest {
this.groupApply = GroupApplyRequest.newBuilder().apply {
this.applierUid = applierUid
this.applierUin = applier
this.groupId = groupCode
this.reason = reason
this.flag = flag
this.type = type
}
})
}.build()
}.build())
return true
}
}
suspend inline fun onMessageEvent(collector: FlowCollector<Pair<MsgRecord, MessageEvent>>) {
messageEventFlow.collect {
MessageEventFlow.collect {
GlobalScope.launch {
collector.emit(it)
}

View File

@ -10,10 +10,6 @@ import io.kritor.file.Folder
import io.kritor.file.GetFileSystemInfoResponse
import io.kritor.file.GetFilesRequest
import io.kritor.file.GetFilesResponse
import io.kritor.file.folder
import io.kritor.file.getFileSystemInfoResponse
import io.kritor.file.getFilesRequest
import io.kritor.file.getFilesResponse
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
@ -73,12 +69,12 @@ internal object GroupFileHelper: QQInterfaces() {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response x2"))
}
return getFileSystemInfoResponse {
return GetFileSystemInfoResponse.newBuilder().apply {
this.fileCount = fileCnt
this.totalCount = limitCnt
this.totalSpace = totalSpace.toInt()
this.usedSpace = usedSpace.toInt()
}
}.build()
}
suspend fun getGroupFiles(groupId: Long, folderId: String = "/"): GetFilesResponse {
@ -108,7 +104,7 @@ internal object GroupFileHelper: QQInterfaces() {
throw StatusRuntimeException(Status.INTERNAL.withDescription("oidb request failed"))
}
val files = arrayListOf<File>()
val dirs = arrayListOf<Folder>()
val folders = arrayListOf<Folder>()
if (fromServiceMsg.wupBuffer != null) {
val oidb = oidb_sso.OIDBSSOPkg().mergeFrom(fromServiceMsg.wupBuffer.slice(4).let {
if (it[0] == 0x78.toByte()) DeflateTools.uncompress(it) else it
@ -119,7 +115,7 @@ internal object GroupFileHelper: QQInterfaces() {
rpt_item_list.get().forEach { file ->
if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FILE) {
val fileInfo = file.file_info
files.add(io.kritor.file.file {
files.add(File.newBuilder().apply {
this.fileId = fileInfo.str_file_id.get()
this.fileName = fileInfo.str_file_name.get()
this.fileSize = fileInfo.uint64_file_size.get()
@ -133,18 +129,18 @@ internal object GroupFileHelper: QQInterfaces() {
this.sha = fileInfo.bytes_sha.get().toByteArray().toHexString()
this.sha3 = fileInfo.bytes_sha3.get().toByteArray().toHexString()
this.md5 = fileInfo.bytes_md5.get().toByteArray().toHexString()
})
}.build())
}
else if (file.uint32_type.get() == oidb_0x6d8.GetFileListRspBody.TYPE_FOLDER) {
val folderInfo = file.folder_info
dirs.add(folder {
folders.add(Folder.newBuilder().apply {
this.folderId = folderInfo.str_folder_id.get()
this.folderName = folderInfo.str_folder_name.get()
this.totalFileCount = folderInfo.uint32_total_file_count.get()
this.createTime = folderInfo.uint32_create_time.get()
this.creator = folderInfo.uint64_create_uin.get()
this.creatorName = folderInfo.str_creator_name.get()
})
}.build())
} else {
LogCenter.log("未知文件类型: ${file.uint32_type.get()}", Level.WARN)
}
@ -154,9 +150,9 @@ internal object GroupFileHelper: QQInterfaces() {
throw StatusRuntimeException(Status.INTERNAL.withDescription("unable to fetch oidb response"))
}
return getFilesResponse {
this.files.addAll(files)
this.folders.addAll(folders)
}
return GetFilesResponse.newBuilder().apply {
this.addAllFiles(files)
this.addAllFolders(folders)
}.build()
}
}

View File

@ -6,14 +6,9 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService
import io.grpc.Status
import io.grpc.StatusRuntimeException
import io.kritor.message.Element
import io.kritor.message.ElementType
import io.kritor.message.ForwardElement
import io.kritor.message.ForwardMessageBody
import io.kritor.message.Scene
import io.kritor.message.forwardElement
import io.kritor.message.nodeOrNull
import io.kritor.message.senderOrNull
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.Level
@ -28,12 +23,12 @@ import qq.service.QQInterfaces
import qq.service.contact.ContactHelper
import qq.service.msg.MessageHelper.getMultiMsg
import qq.service.ticket.TicketHelper
import java.util.UUID
import java.util.*
import kotlin.coroutines.resume
import kotlin.random.Random
import kotlin.time.Duration.Companion.seconds
internal object ForwardMessageHelper: QQInterfaces() {
internal object ForwardMessageHelper : QQInterfaces() {
suspend fun uploadMultiMsg(
chatType: Int,
peerId: String,
@ -47,23 +42,26 @@ internal object ForwardMessageHelper: QQInterfaces() {
val msgs = messages.mapNotNull { msg ->
kotlin.runCatching {
val contact = msg.contact.let {
MessageHelper.generateContact(when(it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer)
MessageHelper.generateContact(
when (it.scene!!) {
Scene.GROUP -> MsgConstant.KCHATTYPEGROUP
Scene.FRIEND -> MsgConstant.KCHATTYPEC2C
Scene.GUILD -> MsgConstant.KCHATTYPEGUILD
Scene.STRANGER_FROM_GROUP -> MsgConstant.KCHATTYPETEMPC2CFROMGROUP
Scene.NEARBY -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.STRANGER -> MsgConstant.KCHATTYPETEMPC2CFROMUNKNOWN
Scene.UNRECOGNIZED -> throw StatusRuntimeException(Status.INVALID_ARGUMENT.withDescription("Unrecognized scene"))
}, it.peer, it.subPeer
)
}
val node = msg.elementsList.find { it.type == ElementType.NODE }?.nodeOrNull
if (node != null) {
val msgId = node.messageId
if (msg.hasMessageId()) {
val record: MsgRecord = withTimeoutOrNull(5000) {
val service = QRoute.api(IMsgService::class.java)
suspendCancellableCoroutine { continuation ->
service.getMsgsByMsgId(contact, arrayListOf(msgId)) { code, _, msgRecords ->
service.getMsgsByMsgId(
contact,
arrayListOf(msg.messageId.toLong())
) { code, _, msgRecords ->
if (code == 0 && msgRecords.isNotEmpty()) {
continuation.resume(msgRecords.first())
} else {
@ -74,7 +72,7 @@ internal object ForwardMessageHelper: QQInterfaces() {
continuation.resume(null)
}
}
} ?: error("合并转发消息节点消息(id = $msgId)获取失败")
} ?: error("合并转发消息节点消息(id = ${msg.messageId})获取失败")
PushMsgBody(
msgHead = ResponseHead(
peerUid = record.senderUid,
@ -121,12 +119,19 @@ internal object ForwardMessageHelper: QQInterfaces() {
)
} else {
PushMsgBody(
msgHead = ResponseHead(
peer = msg.senderOrNull?.uin ?: TicketHelper.getUin().toLong(),
peerUid = msg.senderOrNull?.uid ?: TicketHelper.getUid(),
msgHead = if (msg.hasSender()) ResponseHead(
peer = if (msg.sender.hasUin()) msg.sender.uin else TicketHelper.getUin().toLong(),
peerUid = msg.sender.uid,
receiverUid = TicketHelper.getUid(),
forward = ResponseForward(
friendName = msg.senderOrNull?.nick ?: TicketHelper.getNickname()
friendName = if (msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname()
)
) else ResponseHead(
peer = TicketHelper.getUin().toLong(),
peerUid = TicketHelper.getUid(),
receiverUid = TicketHelper.getUid(),
forward = ResponseForward(
friendName = TicketHelper.getNickname()
)
),
contentHead = ContentHead(
@ -150,7 +155,8 @@ internal object ForwardMessageHelper: QQInterfaces() {
),
body = MsgBody(
richText = msg.elementsList.toRichText(contact).onSuccess {
desc[++i] = (msg.senderOrNull?.nick ?: TicketHelper.getNickname()) + ": " + it.first
desc[++i] =
(if (msg.hasSender() && msg.sender.hasNick()) msg.sender.nick else TicketHelper.getNickname()) + ": " + it.first
}.onFailure {
error("消息合成失败: ${it.stackTraceToString()}")
}.getOrThrow().second
@ -189,15 +195,17 @@ internal object ForwardMessageHelper: QQInterfaces() {
sendInfo = when (chatType) {
MsgConstant.KCHATTYPEC2C -> SendLongMsgInfo(
type = 1,
uid = LongMsgUid(if(peerId.startsWith("u_")) peerId else ContactHelper.getUidByUinAsync(peerId.toLong()) ),
uid = LongMsgUid(if (peerId.startsWith("u_")) peerId else ContactHelper.getUidByUinAsync(peerId.toLong())),
payload = DeflateTools.gzip(payload.toByteArray())
)
MsgConstant.KCHATTYPEGROUP -> SendLongMsgInfo(
type = 3,
uid = LongMsgUid(fromId),
groupUin = fromId.toULong(),
payload = DeflateTools.gzip(payload.toByteArray())
)
else -> throw UnsupportedOperationException("Unsupported chatType: $chatType")
},
setting = LongMsgSettings(
@ -208,8 +216,9 @@ internal object ForwardMessageHelper: QQInterfaces() {
)
).toByteArray()
val fromServiceMsg = sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 60.seconds)
?: return Result.failure(Exception("unable to upload multi message, response timeout"))
val fromServiceMsg =
sendBufferAW("trpc.group.long_msg_interface.MsgService.SsoSendLongMsg", true, req, timeout = 60.seconds)
?: return Result.failure(Exception("unable to upload multi message, response timeout"))
val rsp = runCatching {
fromServiceMsg.wupBuffer.slice(4).decodeProtobuf<LongMsgRsp>()
}.getOrElse {
@ -217,11 +226,11 @@ internal object ForwardMessageHelper: QQInterfaces() {
}
val resId = rsp.sendResult?.resId ?: return Result.failure(Exception("unable to upload multi message"))
return Result.success(forwardElement {
return Result.success(ForwardElement.newBuilder().apply {
this.id = resId
this.summary = summary
this.uniseq = UUID.randomUUID().toString()
this.description = desc.slice(0..if (i < 3) i else 3).joinToString("\n")
})
}.build())
}
}

View File

@ -5,27 +5,7 @@ import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.kernel.nativeinterface.MsgRecord
import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.event.Element
import io.kritor.event.ImageType
import io.kritor.event.Scene
import io.kritor.event.atElement
import io.kritor.event.basketballElement
import io.kritor.event.buttonAction
import io.kritor.event.buttonActionPermission
import io.kritor.event.buttonRender
import io.kritor.event.contactElement
import io.kritor.event.diceElement
import io.kritor.event.faceElement
import io.kritor.event.forwardElement
import io.kritor.event.imageElement
import io.kritor.event.jsonElement
import io.kritor.event.locationElement
import io.kritor.event.pokeElement
import io.kritor.event.replyElement
import io.kritor.event.rpsElement
import io.kritor.event.textElement
import io.kritor.event.videoElement
import io.kritor.event.voiceElement
import io.kritor.message.*
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.helper.ActionMsgException
@ -75,12 +55,12 @@ private object MsgConvertor {
val text = element.textElement
val elem = Element.newBuilder()
if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
elem.setAt(atElement {
elem.setAt(AtElement.newBuilder().apply {
this.uid = text.atNtUid
this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong()
})
} else {
elem.setText(textElement {
elem.setText(TextElement.newBuilder().apply {
this.text = text.content
})
}
@ -91,28 +71,32 @@ private object MsgConvertor {
val face = element.faceElement
val elem = Element.newBuilder()
if (face.faceType == 5) {
elem.setPoke(pokeElement {
elem.setPoke(PokeElement.newBuilder().apply {
this.id = face.vaspokeId
this.type = face.pokeType
this.strength = face.pokeStrength
})
} else {
when(face.faceIndex) {
114 -> elem.setBasketball(basketballElement {
when (face.faceIndex) {
114 -> elem.setBasketball(BasketballElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
358 -> elem.setDice(diceElement {
358 -> elem.setDice(DiceElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
359 -> elem.setRps(rpsElement {
359 -> elem.setRps(RpsElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
394 -> elem.setFace(faceElement {
394 -> elem.setFace(FaceElement.newBuilder().apply {
this.id = face.faceIndex
this.isBig = face.faceType == 3
this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1
})
else -> elem.setFace(faceElement {
else -> elem.setFace(FaceElement.newBuilder().apply {
this.id = face.faceIndex
this.isBig = face.faceType == 3
})
@ -150,7 +134,7 @@ private object MsgConvertor {
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
val elem = Element.newBuilder()
elem.setImage(imageElement {
elem.setImage(ImageElement.newBuilder().apply {
this.file = md5
this.url = when (record.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
@ -190,7 +174,8 @@ private object MsgConvertor {
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
}
this.type = if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON
this.type =
if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON
this.subType = image.picSubType
})
@ -205,10 +190,14 @@ private object MsgConvertor {
ptt.fileName.substring(5)
else ptt.md5HexStr
elem.setVoice(voiceElement {
elem.setVoice(VoiceElement.newBuilder().apply {
this.url = when (record.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl("0", md5.hex2ByteArray(), ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
"0",
md5.hex2ByteArray(),
ptt.fileUuid
)
else -> throw UnsupportedOperationException("Not supported chat type: ${record.chatType}")
}
@ -229,7 +218,7 @@ private object MsgConvertor {
it[it.size - 2].hex2ByteArray()
}
} else video.fileName.split(".")[0].hex2ByteArray()
elem.setVideo(videoElement {
elem.setVideo(VideoElement.newBuilder().apply {
this.file = md5.toHexString()
this.url = when (record.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
@ -244,7 +233,7 @@ private object MsgConvertor {
suspend fun convertMarketFace(record: MsgRecord, element: MsgElement): Result<Element> {
val marketFace = element.marketFaceElement
val elem = Element.newBuilder()
elem.setMarketFace(io.kritor.event.marketFaceElement {
elem.setMarketFace(MarketFaceElement.newBuilder().apply {
this.id = marketFace.emojiId.lowercase()
})
return Result.success(elem.build())
@ -256,8 +245,8 @@ private object MsgConvertor {
when (data["app"].asString) {
"com.tencent.multimsg" -> {
val info = data["meta"].asJsonObject["detail"].asJsonObject
elem.setForward(forwardElement {
this.id = info["resid"].asString
elem.setForward(ForwardElement.newBuilder().apply {
this.resId = info["resid"].asString
this.uniseq = info["uniseq"].asString
this.summary = info["summary"].asString
this.description = info["news"].asJsonArray.joinToString("\n") {
@ -268,7 +257,7 @@ private object MsgConvertor {
"com.tencent.troopsharecard" -> {
val info = data["meta"].asJsonObject["contact"].asJsonObject
elem.setContact(contactElement {
elem.setContact(ContactElement.newBuilder().apply {
this.scene = Scene.GROUP
this.peer = info["jumpUrl"].asString.split("group_code=")[1]
})
@ -276,7 +265,7 @@ private object MsgConvertor {
"com.tencent.contact.lua" -> {
val info = data["meta"].asJsonObject["contact"].asJsonObject
elem.setContact(contactElement {
elem.setContact(ContactElement.newBuilder().apply {
this.scene = Scene.FRIEND
this.peer = info["jumpUrl"].asString.split("uin=")[1]
})
@ -284,7 +273,7 @@ private object MsgConvertor {
"com.tencent.map" -> {
val info = data["meta"].asJsonObject["Location.Search"].asJsonObject
elem.setLocation(locationElement {
elem.setLocation(LocationElement.newBuilder().apply {
this.lat = info["lat"].asString.toFloat()
this.lon = info["lng"].asString.toFloat()
this.address = info["address"].asString
@ -292,7 +281,7 @@ private object MsgConvertor {
})
}
else -> elem.setJson(jsonElement {
else -> elem.setJson(JsonElement.newBuilder().apply {
this.json = data.toString()
})
}
@ -302,14 +291,15 @@ private object MsgConvertor {
suspend fun convertReply(record: MsgRecord, element: MsgElement): Result<Element> {
val reply = element.replyElement
val elem = Element.newBuilder()
elem.setReply(replyElement {
elem.setReply(ReplyElement.newBuilder().apply {
val msgSeq = reply.replayMsgSeq
val contact = MessageHelper.generateContact(record)
val sourceRecords = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
QRoute.api(IMsgService::class.java)
.getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}
if (sourceRecords.isNullOrEmpty()) {
@ -332,11 +322,17 @@ private object MsgConvertor {
val fileSubId = fileMsg.fileSubId ?: ""
val url = when (record.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(record.guildId, record.channelId, fileId, bizId)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(
record.guildId,
record.channelId,
fileId,
bizId
)
else -> RichProtoSvc.getGroupFileDownUrl(record.peerUin, fileId, bizId)
}
val elem = Element.newBuilder()
elem.setFile(io.kritor.event.fileElement {
elem.setFile(FileElement.newBuilder().apply {
this.name = fileName
this.size = fileSize
this.url = url
@ -351,7 +347,7 @@ private object MsgConvertor {
suspend fun convertMarkdown(record: MsgRecord, element: MsgElement): Result<Element> {
val markdown = element.markdownElement
val elem = Element.newBuilder()
elem.setMarkdown(io.kritor.event.markdownElement {
elem.setMarkdown(MarkdownElement.newBuilder().apply {
this.markdown = markdown.content
})
return Result.success(elem.build())
@ -360,7 +356,7 @@ private object MsgConvertor {
suspend fun convertBubbleFace(record: MsgRecord, element: MsgElement): Result<Element> {
val bubbleFace = element.faceBubbleElement
val elem = Element.newBuilder()
elem.setBubbleFace(io.kritor.event.bubbleFaceElement {
elem.setBubbleFace(BubbleFaceElement.newBuilder().apply {
this.id = bubbleFace.yellowFaceInfo.index
this.count = bubbleFace.faceCount ?: 1
})
@ -370,34 +366,34 @@ private object MsgConvertor {
suspend fun convertInlineKeyboard(record: MsgRecord, element: MsgElement): Result<Element> {
val inlineKeyboard = element.inlineKeyboardElement
val elem = Element.newBuilder()
elem.setButton(io.kritor.event.buttonElement {
elem.setButton(ButtonElement.newBuilder().apply {
inlineKeyboard.rows.forEach { row ->
this.rows.add(io.kritor.event.row {
row.buttons.forEach buttonsLoop@ { button ->
this.addRows(ButtonRow.newBuilder().apply {
row.buttons.forEach buttonsLoop@{ button ->
if (button == null) return@buttonsLoop
this.buttons.add(io.kritor.event.button {
this.addButtons(Button.newBuilder().apply {
this.id = button.id
this.action = buttonAction {
this.action = ButtonAction.newBuilder().apply {
this.type = button.type
this.permission = buttonActionPermission {
this.permission = ButtonActionPermission.newBuilder().apply {
this.type = button.permissionType
button.specifyRoleIds?.let {
this.roleIds.addAll(it)
this.addAllRoleIds(it)
}
button.specifyTinyids?.let {
this.userIds.addAll(it)
this.addAllUserIds(it)
}
}
}.build()
this.unsupportedTips = button.unsupportTips ?: ""
this.data = button.data ?: ""
this.reply = button.isReply
this.enter = button.enter
}
this.renderData = buttonRender {
}.build()
this.renderData = ButtonRender.newBuilder().apply {
this.label = button.label ?: ""
this.visitedLabel = button.visitedLabel ?: ""
this.style = button.style
}
}.build()
})
}
})

View File

@ -1,24 +1,10 @@
@file:OptIn(ExperimentalUnsignedTypes::class)
package qq.service.msg
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import io.kritor.message.Element
import io.kritor.message.ElementType
import io.kritor.message.ImageType
import io.kritor.message.Scene
import io.kritor.message.atElement
import io.kritor.message.buttonActionPermission
import io.kritor.message.buttonElement
import io.kritor.message.contactElement
import io.kritor.message.faceElement
import io.kritor.message.forwardElement
import io.kritor.message.imageElement
import io.kritor.message.jsonElement
import io.kritor.message.locationElement
import io.kritor.message.markdownElement
import io.kritor.message.replyElement
import io.kritor.message.textElement
import io.kritor.message.*
import kotlinx.io.core.ByteReadPacket
import kotlinx.io.core.discardExact
import kotlinx.io.core.readUInt
@ -48,142 +34,151 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
val at = ByteReadPacket(text.attr6Buf!!)
at.discardExact(7)
val uin = at.readUInt()
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.AT
this.at = atElement {
this.at = AtElement.newBuilder().apply {
this.uin = uin.toLong()
}
})
}.build()
}.build())
} else {
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.TEXT
this.text = textElement {
this.text = TextElement.newBuilder().apply {
this.text = text.str ?: ""
}
})
}.build()
}.build())
}
} else if (element.face != null) {
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.FACE
this.face = faceElement {
this.face = FaceElement.newBuilder().apply {
this.id = element.face!!.index ?: 0
}
})
}.build()
}.build())
} else if (element.customFace != null) {
val customFace = element.customFace!!
val md5 = customFace.md5.toHexString()
val origUrl = customFace.origUrl!!
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.IMAGE
this.image = imageElement {
this.image = ImageElement.newBuilder().apply {
this.fileName = md5
this.type = if (customFace.origin == true) ImageType.ORIGIN else ImageType.COMMON
this.url = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(origUrl, md5)
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
origUrl,
md5
)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(origUrl, md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(origUrl, md5)
else -> throw UnsupportedOperationException("Not supported chat type: $contact")
}
}
})
}.build()
}.build())
} else if (element.notOnlineImage != null) {
require(element.notOnlineImage != null)
val md5 = element.notOnlineImage!!.picMd5.toHexString()
val origUrl = element.notOnlineImage!!.origUrl!!
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.IMAGE
this.image = imageElement {
this.image = ImageElement.newBuilder().apply {
this.fileName = md5
this.type = if (element.notOnlineImage?.original == true) ImageType.ORIGIN else ImageType.COMMON
this.url = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(origUrl, md5)
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
origUrl,
md5
)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(origUrl, md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(origUrl, md5)
else -> throw UnsupportedOperationException("Not supported chat type: $contact")
}
}
})
}.build()
}.build())
} else if (element.generalFlags != null) {
val generalFlags = element.generalFlags!!
if (generalFlags.longTextFlag == 1u) {
kritorMessages.add(io.kritor.message.element {
this.type = ElementType.FORWARD
this.forward = forwardElement {
this.id = generalFlags.longTextResid ?: ""
}
})
}
// val generalFlags = element.generalFlags!!
// if (generalFlags.longTextFlag == 1u) {
// kritorMessages.add(Element.newBuilder().apply {
// this.type = ElementType.FORWARD
// this.forward = forwardElement {
// this.id = generalFlags.longTextResid ?: ""
// }
// })
// }
} else if (element.srcMsg != null) {
val srcMsg = element.srcMsg!!
val msgId = srcMsg.pbReserve?.msgRand?.toLong() ?: 0
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.REPLY
this.reply = replyElement {
this.reply = ReplyElement.newBuilder().apply {
this.messageId = msgId
}
})
}.build()
}.build())
} else if (element.lightApp != null) {
val data = element.lightApp!!.data!!
val jsonStr = (if (data[0].toInt() == 1) DeflateTools.uncompress(data.slice(1)) else data.slice(1)).decodeToString()
val jsonStr =
(if (data[0].toInt() == 1) DeflateTools.uncompress(data.slice(1)) else data.slice(1)).decodeToString()
val json = jsonStr.asJsonObject
when (json["app"].asString) {
"com.tencent.multimsg" -> {
val info = json["meta"].asJsonObject["detail"].asJsonObject
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.FORWARD
this.forward = forwardElement {
this.id = info["resid"].asString
this.forward = ForwardElement.newBuilder().apply {
this.resId = info["resid"].asString
this.uniseq = info["uniseq"].asString
this.summary = info["summary"].asString
this.description = info["news"].asJsonArray.joinToString("\n") {
it.asJsonObject["text"].asString
}
}
})
}.build()
}.build())
}
"com.tencent.troopsharecard" -> {
val info = json["meta"].asJsonObject["contact"].asJsonObject
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.CONTACT
this.contact = contactElement {
this.contact = ContactElement.newBuilder().apply {
this.scene = Scene.GROUP
this.peer = info["jumpUrl"].asString.split("group_code=")[1]
}
})
}.build()
}.build())
}
"com.tencent.contact.lua" -> {
val info = json["meta"].asJsonObject["contact"].asJsonObject
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.CONTACT
this.contact = contactElement {
this.contact = ContactElement.newBuilder().apply {
this.scene = Scene.FRIEND
this.peer = info["jumpUrl"].asString.split("uin=")[1]
}
})
}.build()
}.build())
}
"com.tencent.map" -> {
val info = json["meta"].asJsonObject["Location.Search"].asJsonObject
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.LOCATION
this.location = locationElement {
this.location = LocationElement.newBuilder().apply {
this.lat = info["lat"].asString.toFloat()
this.lon = info["lng"].asString.toFloat()
this.address = info["address"].asString
this.title = info["name"].asString
}
})
}.build()
}.build())
}
else -> {
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.JSON
this.json = jsonElement {
this.json = JsonElement.newBuilder().apply {
this.json = jsonStr
}
})
}.build()
}.build())
}
}
} else if (element.commonElem != null) {
@ -192,81 +187,78 @@ suspend fun List<Elem>.toKritorResponseMessages(contact: Contact): ArrayList<Ele
37 -> {
val qFaceExtra = commonElem.elem!!.decodeProtobuf<QFaceExtra>()
when (qFaceExtra.faceId) {
358 -> kritorMessages.add(io.kritor.message.element {
358 -> kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.DICE
this.dice = io.kritor.message.diceElement {
this.dice = DiceElement.newBuilder().apply {
this.id = qFaceExtra.result!!.toInt()
}
})
}.build()
}.build())
359 -> kritorMessages.add(io.kritor.message.element {
359 -> kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.RPS
this.rps = io.kritor.message.rpsElement {
this.rps = RpsElement.newBuilder().apply {
this.id = qFaceExtra.result!!.toInt()
}
})
}.build()
}.build())
else -> kritorMessages.add(io.kritor.message.element {
else -> kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.FACE
this.face = faceElement {
this.face = FaceElement.newBuilder().apply {
this.id = qFaceExtra.faceId ?: 0
this.isBig = false
this.result = qFaceExtra.result?.toInt() ?: 0
}
})
}.build()
}.build())
}
}
45 -> {
val markdownExtra = commonElem.elem!!.decodeProtobuf<MarkdownExtra>()
kritorMessages.add(io.kritor.message.element {
kritorMessages.add(Element.newBuilder().apply {
this.type = ElementType.MARKDOWN
this.markdown = markdownElement {
this.markdown = MarkdownElement.newBuilder().apply {
this.markdown = markdownExtra.content!!
}
})
}.build()
}.build())
}
46 -> {
val buttonExtra = commonElem.elem!!.decodeProtobuf<ButtonExtra>()
kritorMessages.add(io.kritor.message.element {
this.type = ElementType.BUTTON
this.button = buttonElement {
buttonExtra.field1!!.rows?.forEach { row ->
this.rows.add(io.kritor.message.row {
row.buttons?.forEach { button ->
this.buttons.add(io.kritor.message.button {
val renderData = button.renderData
val action = button.action
val permission = action?.permission
this.id = button.id ?: ""
this.renderData = io.kritor.message.buttonRender {
this.label = renderData?.label ?: ""
this.visitedLabel = renderData?.visitedLabel ?: ""
this.style = renderData?.style ?: 0
}
this.action = io.kritor.message.buttonAction {
this.type = action?.type ?: 0
this.permission = buttonActionPermission {
this.type = permission?.type ?: 0
this.roleIds.addAll(
permission?.specifyRoleIds ?: emptyList()
)
this.userIds.addAll(
permission?.specifyUserIds ?: emptyList()
)
}
this.unsupportedTips = action?.unsupportTips ?: ""
this.data = action?.data ?: ""
this.reply = action?.reply ?: false
this.enter = action?.enter ?: false
}
})
}
})
}
}
})
kritorMessages.add(
Element.newBuilder().setButton(ButtonElement.newBuilder().apply {
this.addAllRows(buttonExtra.field1!!.rows!!.map { row ->
ButtonRow.newBuilder().apply {
this.addAllButtons(row.buttons!!.map { button ->
Button.newBuilder().apply {
this.id = button.id
this.renderData = ButtonRender.newBuilder().apply {
this.label = button.renderData?.label ?: ""
this.visitedLabel = button.renderData?.visitedLabel ?: ""
this.style = button.renderData?.style ?: 0
}.build()
this.action = ButtonAction.newBuilder().apply {
this.type = button.action?.type?:0
this.permission = ButtonActionPermission.newBuilder().apply {
this.type = button.action?.permission?.type?:0
button.action?.permission?.specifyRoleIds?.let {
this.addAllRoleIds(it)
}
button.action?.permission?.specifyUserIds?.let {
this.addAllUserIds(it)
}
}.build()
this.unsupportedTips = button.action?.unsupportTips ?: ""
this.data = button.action?.data ?: ""
this.reply = button.action?.reply ?: false
this.enter = button.action?.enter ?: false
}.build()
}.build()
})
}.build()
})
this.applicationId = buttonExtra.field1?.appid?.toLong() ?: 0L
}.build()).build()
)
}
}
}

View File

@ -1,8 +1,9 @@
package qq.service.msg
import com.tencent.mobileqq.qroute.QRoute
import com.tencent.qqnt.kernel.nativeinterface.*
import com.tencent.qqnt.kernel.nativeinterface.Contact
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
import com.tencent.qqnt.kernel.nativeinterface.MsgElement
import com.tencent.qqnt.msg.api.IMsgService
import io.kritor.message.*
import kotlinx.coroutines.suspendCancellableCoroutine
@ -54,12 +55,12 @@ private object ReqMsgConvertor {
val text = element.textElement
val elem = Element.newBuilder()
if (text.atType != MsgConstant.ATTYPEUNKNOWN) {
elem.setAt(atElement {
elem.setAt(AtElement.newBuilder().apply {
this.uid = text.atNtUid
this.uin = ContactHelper.getUinByUidAsync(text.atNtUid).toLong()
})
} else {
elem.setText(textElement {
elem.setText(TextElement.newBuilder().apply {
this.text = text.content
})
}
@ -70,28 +71,32 @@ private object ReqMsgConvertor {
val face = element.faceElement
val elem = Element.newBuilder()
if (face.faceType == 5) {
elem.setPoke(pokeElement {
elem.setPoke(PokeElement.newBuilder().apply {
this.id = face.vaspokeId
this.type = face.pokeType
this.strength = face.pokeStrength
})
} else {
when(face.faceIndex) {
114 -> elem.setBasketball(basketballElement {
when (face.faceIndex) {
114 -> elem.setBasketball(BasketballElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
358 -> elem.setDice(diceElement {
358 -> elem.setDice(DiceElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
359 -> elem.setRps(rpsElement {
359 -> elem.setRps(RpsElement.newBuilder().apply {
this.id = face.resultId.ifNullOrEmpty { "0" }?.toInt() ?: 0
})
394 -> elem.setFace(faceElement {
394 -> elem.setFace(FaceElement.newBuilder().apply {
this.id = face.faceIndex
this.isBig = face.faceType == 3
this.result = face.resultId.ifNullOrEmpty { "1" }?.toInt() ?: 1
})
else -> elem.setFace(faceElement {
else -> elem.setFace(FaceElement.newBuilder().apply {
this.id = face.faceIndex
this.isBig = face.faceType == 3
})
@ -129,7 +134,7 @@ private object ReqMsgConvertor {
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
val elem = Element.newBuilder()
elem.setImage(imageElement {
elem.setImage(ImageElement.newBuilder().apply {
this.file = md5
this.url = when (contact.chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
@ -164,12 +169,13 @@ private object ReqMsgConvertor {
sha = "",
fileSize = image.fileSize.toULong(),
peer = contact.longPeer().toString(),
subPeer ="0"
subPeer = "0"
)
else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}")
}
this.type = if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON
this.type =
if (image.isFlashPic == true) ImageType.FLASH else if (image.original) ImageType.ORIGIN else ImageType.COMMON
this.subType = image.picSubType
})
@ -184,10 +190,14 @@ private object ReqMsgConvertor {
ptt.fileName.substring(5)
else ptt.md5HexStr
elem.setVoice(voiceElement {
elem.setVoice(VoiceElement.newBuilder().apply {
this.url = when (contact.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPttDownUrl("0", ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl("0", md5.hex2ByteArray(), ptt.fileUuid)
MsgConstant.KCHATTYPEGROUP, MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGroupPttDownUrl(
"0",
md5.hex2ByteArray(),
ptt.fileUuid
)
else -> throw UnsupportedOperationException("Not supported chat type: ${contact.chatType}")
}
@ -208,7 +218,7 @@ private object ReqMsgConvertor {
it[it.size - 2].hex2ByteArray()
}
} else video.fileName.split(".")[0].hex2ByteArray()
elem.setVideo(videoElement {
elem.setVideo(VideoElement.newBuilder().apply {
this.file = md5.toHexString()
this.url = when (contact.chatType) {
MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupVideoDownUrl("0", md5, video.fileUuid)
@ -232,8 +242,8 @@ private object ReqMsgConvertor {
when (data["app"].asString) {
"com.tencent.multimsg" -> {
val info = data["meta"].asJsonObject["detail"].asJsonObject
elem.setForward(forwardElement {
this.id = info["resid"].asString
elem.setForward(ForwardElement.newBuilder().apply {
this.resId = info["resid"].asString
this.uniseq = info["uniseq"].asString
this.summary = info["summary"].asString
this.description = info["news"].asJsonArray.joinToString("\n") {
@ -244,7 +254,7 @@ private object ReqMsgConvertor {
"com.tencent.troopsharecard" -> {
val info = data["meta"].asJsonObject["contact"].asJsonObject
elem.setContact(contactElement {
elem.setContact(ContactElement.newBuilder().apply {
this.scene = Scene.GROUP
this.peer = info["jumpUrl"].asString.split("group_code=")[1]
})
@ -252,7 +262,7 @@ private object ReqMsgConvertor {
"com.tencent.contact.lua" -> {
val info = data["meta"].asJsonObject["contact"].asJsonObject
elem.setContact(contactElement {
elem.setContact(ContactElement.newBuilder().apply {
this.scene = Scene.FRIEND
this.peer = info["jumpUrl"].asString.split("uin=")[1]
})
@ -260,7 +270,7 @@ private object ReqMsgConvertor {
"com.tencent.map" -> {
val info = data["meta"].asJsonObject["Location.Search"].asJsonObject
elem.setLocation(locationElement {
elem.setLocation(LocationElement.newBuilder().apply {
this.lat = info["lat"].asString.toFloat()
this.lon = info["lng"].asString.toFloat()
this.address = info["address"].asString
@ -268,7 +278,7 @@ private object ReqMsgConvertor {
})
}
else -> elem.setJson(jsonElement {
else -> elem.setJson(JsonElement.newBuilder().apply {
this.json = data.toString()
})
}
@ -278,13 +288,14 @@ private object ReqMsgConvertor {
suspend fun convertReply(contact: Contact, element: MsgElement): Result<Element> {
val reply = element.replyElement
val elem = Element.newBuilder()
elem.setReply(replyElement {
elem.setReply(ReplyElement.newBuilder().apply {
val msgSeq = reply.replayMsgSeq
val sourceRecords = withTimeoutOrNull(3000) {
suspendCancellableCoroutine {
QRoute.api(IMsgService::class.java).getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
QRoute.api(IMsgService::class.java)
.getMsgsBySeqAndCount(contact, msgSeq, 1, true) { _, _, records ->
it.resume(records)
}
}
}
if (sourceRecords.isNullOrEmpty()) {
@ -307,11 +318,17 @@ private object ReqMsgConvertor {
val fileSubId = fileMsg.fileSubId ?: ""
val url = when (contact.chatType) {
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CFileDownUrl(fileId, fileSubId)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(contact.guildId, contact.longPeer().toString(), fileId, bizId)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildFileDownUrl(
contact.guildId,
contact.longPeer().toString(),
fileId,
bizId
)
else -> RichProtoSvc.getGroupFileDownUrl(contact.longPeer(), fileId, bizId)
}
val elem = Element.newBuilder()
elem.setFile(fileElement {
elem.setFile(FileElement.newBuilder().apply {
this.name = fileName
this.size = fileSize
this.url = url
@ -326,7 +343,7 @@ private object ReqMsgConvertor {
suspend fun convertMarkdown(contact: Contact, element: MsgElement): Result<Element> {
val markdown = element.markdownElement
val elem = Element.newBuilder()
elem.setMarkdown(markdownElement {
elem.setMarkdown(MarkdownElement.newBuilder().apply {
this.markdown = markdown.content
})
return Result.success(elem.build())
@ -335,7 +352,7 @@ private object ReqMsgConvertor {
suspend fun convertBubbleFace(contact: Contact, element: MsgElement): Result<Element> {
val bubbleFace = element.faceBubbleElement
val elem = Element.newBuilder()
elem.setBubbleFace(bubbleFaceElement {
elem.setBubbleFace(BubbleFaceElement.newBuilder().apply {
this.id = bubbleFace.yellowFaceInfo.index
this.count = bubbleFace.faceCount ?: 1
})
@ -345,38 +362,38 @@ private object ReqMsgConvertor {
suspend fun convertInlineKeyboard(contact: Contact, element: MsgElement): Result<Element> {
val inlineKeyboard = element.inlineKeyboardElement
val elem = Element.newBuilder()
elem.setButton(buttonElement {
inlineKeyboard.rows.forEach { row ->
this.rows.add(row {
row.buttons.forEach buttonsLoop@ { button ->
if (button == null) return@buttonsLoop
this.buttons.add(button {
elem.setButton(ButtonElement.newBuilder().apply {
this.addAllRows(inlineKeyboard.rows.map { row ->
ButtonRow.newBuilder().apply {
this.addAllButtons(row.buttons.map { button ->
Button.newBuilder().apply {
this.id = button.id
this.action = buttonAction {
this.renderData = ButtonRender.newBuilder().apply {
this.label = button.label ?: ""
this.visitedLabel = button.visitedLabel ?: ""
this.style = button.style
}.build()
this.action = ButtonAction.newBuilder().apply {
this.type = button.type
this.permission = buttonActionPermission {
this.permission = ButtonActionPermission.newBuilder().apply {
this.type = button.permissionType
button.specifyRoleIds?.let {
this.roleIds.addAll(it)
this.addAllRoleIds(it)
}
button.specifyTinyids?.let {
this.userIds.addAll(it)
this.addAllUserIds(it)
}
}
}.build()
this.unsupportedTips = button.unsupportTips ?: ""
this.data = button.data ?: ""
this.reply = button.isReply
this.enter = button.enter
}
this.renderData = buttonRender {
this.label = button.label ?: ""
this.visitedLabel = button.visitedLabel ?: ""
this.style = button.style
}
})
}
})
}
}.build()
}.build()
})
}.build()
})
this.applicationId = inlineKeyboard.botAppid
})
return Result.success(elem.build())
}

View File

@ -443,7 +443,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
ElementType.GIFT -> throw UnsupportedOperationException("Unsupported ElementType.GIFT")
ElementType.MARKET_FACE -> throw UnsupportedOperationException("Unsupported ElementType.MARKET_FACE")
ElementType.FORWARD -> {
val resId = it.forward.id
val resId = it.forward.resId
val filename = UUID.randomUUID().toString().uppercase()
var content = it.forward.summary
val descriptions = it.forward.description
@ -552,7 +552,7 @@ suspend fun List<Element>.toRichText(contact: Contact): Result<Pair<String, Rich
)
})
},
appid = 0
appid = it.button.applicationId.toULong()
)
).toByteArray(),
businessType = 1