5 Commits

Author SHA1 Message Date
46ed966c18 Shamrock: recommend Lagrange
Signed-off-by: 白池 <whitechi73@outlook.com>
2024-02-24 01:01:41 +08:00
623dc5da07 Shamrock: fix all multimedia pic fetch
Signed-off-by: 白池 <whitechi73@outlook.com>
2024-02-24 00:34:28 +08:00
bd6d4f046c Shamrock: Remove rkey cache
Signed-off-by: 白池 <whitechi73@outlook.com>
2024-02-23 20:52:44 +08:00
9ad66f2f92 Shamrock: fix #251
Signed-off-by: 白池 <whitechi73@outlook.com>
2024-02-23 19:33:36 +08:00
b4c40e236a Shamrock: 快速序列化/反序列化 Protobuf
Signed-off-by: 白池 <whitechi73@outlook.com>
2024-02-23 18:23:02 +08:00
60 changed files with 1016 additions and 216 deletions

View File

@ -29,6 +29,7 @@
- 一键移植:本项目基于 go-cqhttp 的文档进行开发实现。
- 平行部署:可多平台部署,未来将会支持 Docker 部署的教程。
- 替代方案:[Lagrange.Core](https://github.com/LagrangeDev/Lagrange.Core)
## 权限声明

View File

@ -7,3 +7,8 @@ java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
dependencies {
implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
}

View File

@ -0,0 +1,16 @@
package moe.fuqiuluo.symbols
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import kotlin.reflect.KClass
interface Protobuf<T: Protobuf<T>>
inline fun <reified T: Protobuf<T>> KClass<T>.decode(data: ByteArray): T {
return ProtoBuf.decodeFromByteArray(data)
}
inline fun <reified T: Protobuf<T>> ByteArray.decodeProtobuf(to: KClass<T>? = null): T {
return ProtoBuf.decodeFromByteArray(this)
}

View File

@ -1,6 +1,7 @@
plugins {
kotlin("jvm")
id("com.google.devtools.ksp") version "1.9.21-1.0.15"
kotlin("plugin.serialization") version "1.9.21"
}
ksp {
@ -14,5 +15,8 @@ dependencies {
implementation("com.google.devtools.ksp:symbol-processing-api:1.9.21-1.0.15")
implementation("com.squareup:kotlinpoet:1.14.2")
implementation(DEPENDENCY_PROTOBUF)
implementation(kotlinx("serialization-protobuf", "1.6.2"))
ksp("dev.zacsweers.autoservice:auto-service-ksp:1.1.0")
}

View File

@ -6,6 +6,7 @@ package moe.fuqiuluo.ksp.impl
import com.google.devtools.ksp.KspExperimental
import com.google.devtools.ksp.getAnnotationsByType
import com.google.devtools.ksp.getClassDeclarationByName
import com.google.devtools.ksp.getKotlinClassByName
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
@ -26,18 +27,18 @@ class OneBotHandlerProcessor(
): SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val ActionManagerNode = resolver.getClassDeclarationByName("moe.fuqiuluo.shamrock.remote.action.ActionManager")
if (ActionManagerNode == null) {
logger.error("OneBotHandlerProcessor: ActionManager not found")
return emptyList()
}
?: resolver.getKotlinClassByName("moe.fuqiuluo.shamrock.remote.action.ActionManager")
?: resolver.getClassDeclarationByName("ActionManager")
val symbols = resolver.getSymbolsWithAnnotation(OneBotHandler::class.qualifiedName!!)
val unableToProcess = symbols.filterNot { it.validate() }
val oneBotHandlers = (symbols.filter {
it is KSClassDeclaration && it.validate() && it.classKind == ClassKind.OBJECT
} as Sequence<KSClassDeclaration>).toList()
if (ActionManagerNode != null) {
val oneBotHandlers = (symbols.filter {
it is KSClassDeclaration && it.validate() && it.classKind == ClassKind.OBJECT
} as Sequence<KSClassDeclaration>).toList()
if (oneBotHandlers.isNotEmpty()) {
ActionManagerNode.accept(ActionManagerVisitor(oneBotHandlers), Unit)
if (oneBotHandlers.isNotEmpty()) {
ActionManagerNode.accept(ActionManagerVisitor(oneBotHandlers), Unit)
}
}
return unableToProcess.toList()

View File

@ -0,0 +1,77 @@
@file:Suppress("UNCHECKED_CAST")
package moe.fuqiuluo.ksp.impl
import com.google.devtools.ksp.isInternal
import com.google.devtools.ksp.isPrivate
import com.google.devtools.ksp.processing.CodeGenerator
import com.google.devtools.ksp.processing.Dependencies
import com.google.devtools.ksp.processing.KSPLogger
import com.google.devtools.ksp.processing.Resolver
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.symbol.ClassKind
import com.google.devtools.ksp.symbol.KSAnnotated
import com.google.devtools.ksp.symbol.KSClassDeclaration
import com.google.devtools.ksp.symbol.KSDeclaration
import com.google.devtools.ksp.validate
import com.squareup.kotlinpoet.FileSpec
import kotlinx.serialization.Serializable
class ProtobufProcessor(
private val codeGenerator: CodeGenerator,
private val logger: KSPLogger
): SymbolProcessor {
override fun process(resolver: Resolver): List<KSAnnotated> {
val symbols = resolver.getSymbolsWithAnnotation(Serializable::class.qualifiedName!!)
val unableToProcess = symbols.filterNot { it.validate() }
val actions = (symbols.filter {
it is KSClassDeclaration && it.validate() && it.classKind == ClassKind.CLASS
} as Sequence<KSClassDeclaration>).filter {
it.superTypes.any { superType ->
superType.resolve().declaration.qualifiedName?.asString() == "moe.fuqiuluo.symbols.Protobuf"
}
}.toList()
if (actions.isNotEmpty()) {
actions.forEachIndexed { index, clz ->
if (clz.isInternal()) return@forEachIndexed
if (clz.isPrivate()) return@forEachIndexed
val packageName = "protobuf.auto"
val fileSpecBuilder = FileSpec.scriptBuilder("FastProtobuf", packageName)
fileSpecBuilder.addImport("kotlinx.serialization.protobuf", "ProtoBuf")
fileSpecBuilder.addImport("kotlinx.serialization", "decodeFromByteArray")
fileSpecBuilder.addImport("kotlinx.serialization", "encodeToByteArray")
if (clz.parentDeclaration != null) {
fileSpecBuilder.addImport(clz.importPackage, clz.simpleName.asString())
} else {
fileSpecBuilder.addImport(clz.packageName.asString(), clz.simpleName.asString())
}
if (clz.typeParameters.isNotEmpty()) {
val genericType = clz.typeParameters.joinToString(", ") { it.name.asString() }
fileSpecBuilder.addStatement("""inline fun <$genericType> ${clz.simpleName.asString()}<$genericType>.toByteArray() = ProtoBuf.encodeToByteArray(this)""")
} else {
fileSpecBuilder.addStatement("inline fun ${clz.simpleName.asString()}.toByteArray() = ProtoBuf.encodeToByteArray(this)")
}
codeGenerator.createNewFile(
dependencies = Dependencies.ALL_FILES,
packageName = packageName,
fileName = "FP${clz.simpleName.asString().hashCode()}"
).use { outputStream ->
outputStream.writer().use {
fileSpecBuilder.build().writeTo(it)
}
}
}
}
return unableToProcess.toList()
}
private val KSDeclaration.importPackage: String
get() = if (parentDeclaration != null) {
parentDeclaration!!.importPackage + "." + parentDeclaration!!.simpleName.asString()
} else packageName.asString()
}

View File

@ -0,0 +1,17 @@
package moe.fuqiuluo.ksp.providers
import com.google.auto.service.AutoService
import com.google.devtools.ksp.processing.SymbolProcessor
import com.google.devtools.ksp.processing.SymbolProcessorEnvironment
import com.google.devtools.ksp.processing.SymbolProcessorProvider
import moe.fuqiuluo.ksp.impl.ProtobufProcessor
@AutoService(SymbolProcessorProvider::class)
class ProtobufProvider: SymbolProcessorProvider {
override fun create(environment: SymbolProcessorEnvironment): SymbolProcessor {
return ProtobufProcessor(
environment.codeGenerator,
environment.logger
)
}
}

View File

@ -2,6 +2,7 @@ plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
kotlin("plugin.serialization") version "1.9.21"
id("com.google.devtools.ksp") version "1.9.21-1.0.15"
}
android {
@ -38,4 +39,7 @@ dependencies {
implementation(kotlinx("serialization-protobuf", "1.6.2"))
implementation(kotlinx("serialization-json", "1.6.2"))
implementation(project(":annotations"))
ksp(project(":processor"))
}

View File

@ -2,9 +2,10 @@ package protobuf.fav
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class WeiyunComm(
@ProtoNumber(1) val req: WeiyunCommonReq? = null,
@ProtoNumber(2) val resp: WeiyunCommonResp? = null
)
): Protobuf<WeiyunComm>

View File

@ -6,6 +6,7 @@ package protobuf.fav
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class WeiyunMsgHead(
@ -27,4 +28,5 @@ data class WeiyunMsgHead(
@ProtoNumber(103) val promptMsg: String? = null,
@ProtoNumber(111) val totalSpace: ULong = ULong.MIN_VALUE,
@ProtoNumber(112) val usedSpace: ULong = ULong.MIN_VALUE,
)
): Protobuf<WeiyunMsgHead>

View File

@ -6,6 +6,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
import protobuf.qweb.QWebExtInfo
@Serializable
@ -19,7 +20,7 @@ data class GetGuildFeedsReq(
@ProtoNumber(7) var u7: Int? = null,
@ProtoNumber(8) var u8: Int? = null,
@ProtoNumber(9) var u9: ByteArray? = null,
)
): Protobuf<GetGuildFeedsReq>
@Serializable
data class GetGuildFeedsRsp(
@ -27,7 +28,7 @@ data class GetGuildFeedsRsp(
@ProtoNumber(2) var isFinish: Int = 0,
//@ProtoNumber(3) var feedAttchInfo: ByteArray? = null,
//@ProtoNumber(4) var traceId: String? = null,
)
): Protobuf<GetGuildFeedsRsp>
@Serializable
data class StFeed(

View File

@ -0,0 +1,37 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.lightapp
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class AdaptShareInfoReq(
//@ProtoNumber(1) var extInfo: Any? = null,
@ProtoNumber(2) var appid: String? = null,
@ProtoNumber(3) var title: String? = null,
@ProtoNumber(4) var desc: String? = null,
@ProtoNumber(5) var time: ULong? = null,
@ProtoNumber(6) var scene: UInt? = null,
@ProtoNumber(7) var templetType: UInt? = null,
@ProtoNumber(8) var businessType: UInt? = null,
@ProtoNumber(9) var picUrl: String? = null,
@ProtoNumber(10) var vidUrl: String? = null,
@ProtoNumber(11) var jumpUrl: String? = null,
@ProtoNumber(12) var iconUrl: String? = null,
@ProtoNumber(13) var verType: UInt? = null,
@ProtoNumber(14) var shareType: UInt? = null,
@ProtoNumber(15) var versionId: String? = null,
@ProtoNumber(16) var withShareTicket: UInt? = null,
@ProtoNumber(17) var webURL: String? = null,
//@ProtoNumber(18) var appidRich: Any? = null,
@ProtoNumber(19) var template: Template? = null,
//@ProtoNumber(20) var rcvOpenId: Any? = null,
): Protobuf<AdaptShareInfoReq>
@Serializable
data class Template(
@ProtoNumber(1) var templateId: UInt? = null,
@ProtoNumber(2) var templateData: ByteArray? = null,
)

View File

@ -4,6 +4,7 @@ package protobuf.message.longmsg
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
@ -39,14 +40,14 @@ data class LongMsgReq(
@ProtoNumber(1) val recvInfo: RecvLongMsgInfo? = null,
@ProtoNumber(2) val sendInfo: SendLongMsgInfo? = null,
@ProtoNumber(15) val setting: LongMsgSettings? = null,
)
): Protobuf<LongMsgReq>
@Serializable
data class LongMsgRsp(
@ProtoNumber(1) val recvResult: RecvLongMsgResult? = null,
@ProtoNumber(2) val sendResult: SendLongMsgResult? = null,
@ProtoNumber(15) val setting: LongMsgSettings? = null
) {
): Protobuf<LongMsgRsp> {
companion object {
@Serializable
data class SendLongMsgResult(

View File

@ -5,6 +5,7 @@ package protobuf.message.longmsg
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
import protobuf.message.MessageBody
import protobuf.message.MessageContent
import protobuf.message.MessageHead
@ -29,4 +30,4 @@ data class LongMsgAction(
@Serializable
data class LongMsgPayload(
@ProtoNumber(2) val action: List<LongMsgAction>? = null
)
): Protobuf<LongMsgPayload>

View File

@ -4,12 +4,13 @@ package protobuf.message.multimedia
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class RichMediaForPicData(
@ProtoNumber(1) val info: MediaInfo?,
@ProtoNumber(2) val display: DisplayMediaInfo?,
) {
): Protobuf<RichMediaForPicData> {
companion object {
@Serializable
data class MediaInfo(

View File

@ -2,6 +2,7 @@ package protobuf.msg
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
class PbSendMsgReq(
@ -19,7 +20,7 @@ class PbSendMsgReq(
//@ProtoNumber(12) var msgCtrl: MsgCtrl? = null,
//@ProtoNumber(13) var receipt_req: ReceiptReq? = null,
//@ProtoNumber(14) var multi_send_seq: UInt = 0u,
)
): Protobuf<PbSendMsgReq>
@Serializable
data class ContentHead(

View File

@ -2,11 +2,13 @@ package protobuf.oidb
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class TrpcOidb(
@ProtoNumber(1) val cmd: Int = Int.MIN_VALUE,
@ProtoNumber(2) val service: Int = Int.MIN_VALUE,
@ProtoNumber(4) val buffer: ByteArray,
//@ProtoNumber(11) val traceParams: Map<String, String> = mapOf(),
@ProtoNumber(12) val flag: Int = Int.MIN_VALUE,
)
): Protobuf<TrpcOidb>

View File

@ -0,0 +1,272 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.oidb.cmd0x11c5
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class NtV2RichMediaReq(
@ProtoNumber(1) val head: MultiMediaReqHead,
@ProtoNumber(2) val upload: UploadReq? = null, // 100
@ProtoNumber(3) val download: DownloadReq? = null,
@ProtoNumber(4) val downloadRkey: DownloadRkeyReq? = null,
@ProtoNumber(5) val delete: DeleteReq? = null,
@ProtoNumber(6) val uploadCompleted: UploadCompletedReq? = null,
@ProtoNumber(7) val msgInfoAuth: MsgInfoAuthReq? = null,
@ProtoNumber(8) val uploadKeyRenewal: UploadKeyRenewalReq? = null,
@ProtoNumber(9) val downloadSafe: DownloadSafeReq? = null,
@ProtoNumber(99) val extension: ByteArray? = null,
): Protobuf<NtV2RichMediaReq>
@Serializable
data class DownloadSafeReq(
@ProtoNumber(1) val index: IndexNode,
)
@Serializable
data class UploadKeyRenewalReq(
@ProtoNumber(1) val oldUkey: String,
@ProtoNumber(2) val subType: UInt,
)
@Serializable
data class MsgInfoAuthReq(
@ProtoNumber(1) val msg: ByteArray,
@ProtoNumber(2) val authTime: ULong,
)
@Serializable
data class UploadCompletedReq(
@ProtoNumber(1) val srvSendMsg: Boolean,
@ProtoNumber(2) val clientRandomId: ULong,
@ProtoNumber(3) val msgInfo: MsgInfo,
@ProtoNumber(4) val clientSeq: UInt,
)
@Serializable
data class MsgInfo(
@ProtoNumber(1) val msgInfoBody: List<MsgInfoBody>,
@ProtoNumber(2) val extBizInfo: ExtBizInfo,
)
@Serializable
data class MsgInfoBody(
@ProtoNumber(1) val index: IndexNode? = null,
@ProtoNumber(2) val picture: PictureInfo? = null,
@ProtoNumber(3) val video: VideoInfo? = null,
@ProtoNumber(4) val audio: AudioInfo? = null,
@ProtoNumber(5) val fileExist: Boolean? = null,
@ProtoNumber(6) val hashSum: ByteArray? = null,
)
@Serializable
class VideoInfo
@Serializable
class AudioInfo
@Serializable
data class PictureInfo(
@ProtoNumber(1) val urlPath: String,
@ProtoNumber(2) val ext: PicUrlExtInfo? = null,
@ProtoNumber(3) val domain: String? = null
)
@Serializable
data class PicUrlExtInfo(
@ProtoNumber(1) val originalParameter: String? = null,
@ProtoNumber(2) val bigParameter: String? = null,
@ProtoNumber(3) val thumbParameter: String? = null
)
@Serializable
data class DeleteReq(
@ProtoNumber(1) val index: List<IndexNode>,
@ProtoNumber(2) val needRecallMsg: Boolean? = null,
@ProtoNumber(3) val msgSeq: ULong? = null,
@ProtoNumber(4) val msgRandom: ULong? = null,
@ProtoNumber(5) val msgTime: ULong? = null,
)
@Serializable
data class DownloadRkeyReq(
@ProtoNumber(1) val types: List<Int>
)
@Serializable
data class UploadReq(
@ProtoNumber(1) val uploadInfo: List<UploadInfo>,
@ProtoNumber(2) val tryFastUploadCompleted: Boolean? = null,
@ProtoNumber(3) val srvSendMsg: Boolean? = null,
@ProtoNumber(4) val clientRandomId: ULong = ULong.MIN_VALUE,
@ProtoNumber(5) val compatQMsgSceneType: UInt? = null,
@ProtoNumber(6) val extBizInfo: ExtBizInfo? = null,
@ProtoNumber(7) val clientSeq: UInt? = null,
@ProtoNumber(8) val noNeedCompatMsg: Boolean = false,
)
@Serializable
data class ExtBizInfo(
@ProtoNumber(1) val pic: PicExtBizInfo? = null,
@ProtoNumber(2) val video: VideoExtBizInfo? = null,
@ProtoNumber(3) val ptt: PttExtBizInfo? = null,
@ProtoNumber(10) val busiType: UInt,
)
@Serializable
data class PttExtBizInfo(
@ProtoNumber(1) val srcUin: ULong,
@ProtoNumber(2) val pttScene: UInt,
@ProtoNumber(3) val pttType: UInt,
@ProtoNumber(4) val changeVoice: UInt,
@ProtoNumber(5) val waveform: ByteArray? = null,
@ProtoNumber(6) val autoConvertText: UInt? = null,
@ProtoNumber(11) val bytesReserve: ByteArray? = null,
@ProtoNumber(12) val bytesPbReserve: ByteArray? = null,
@ProtoNumber(13) val bytesGeneralFlags: ByteArray? = null,
)
@Serializable
data class VideoExtBizInfo(
@ProtoNumber(1) val fromScene: UInt,
@ProtoNumber(2) val toScene: UInt,
@ProtoNumber(3) val bytesPbReserve: ByteArray,
)
@Serializable
data class PicExtBizInfo(
@ProtoNumber(1) val bizType: UInt,
@ProtoNumber(2) val textSummary: String,
@ProtoNumber(11) val bytesPbReserveC2c: ByteArray? = null,
@ProtoNumber(12) val bytesPbReserveTroop: ByteArray? = null,
@ProtoNumber(1001) val fromScene: UInt? = null,
@ProtoNumber(1002) val toScene: UInt? = null,
@ProtoNumber(1003) val oldFileId: UInt? = null,
)
@Serializable
data class UploadInfo(
@ProtoNumber(1) val fileInfo: FileInfo,
@ProtoNumber(2) val subFileType: UInt
)
@Serializable
data class FileInfo(
@ProtoNumber(1) val fileSize: ULong,
@ProtoNumber(2) val md5: String,
@ProtoNumber(3) val sha1: String,
@ProtoNumber(4) val name: String,
@ProtoNumber(5) val fileType: FileType,
@ProtoNumber(6) val width: UInt,
@ProtoNumber(7) val height: UInt,
@ProtoNumber(8) val time: UInt,
@ProtoNumber(9) val original: UInt,
)
@Serializable
data class FileType(
@ProtoNumber(1) val fileType: UInt = 0u,
@ProtoNumber(2) val picFormat: UInt = 0u,
@ProtoNumber(3) val videoFormat: UInt? = null,
@ProtoNumber(4) val voiceFormat: UInt? = null,
)
@Serializable
data class DownloadReq(
@ProtoNumber(1) val index: IndexNode,
@ProtoNumber(2) val ext: DownloadExt,
)
@Serializable
data class DownloadExt(
@ProtoNumber(1) val pic: PicDownloadExt? = null,
@ProtoNumber(2) val video: VideoDownloadExt,
@ProtoNumber(3) val voice: PttDownloadExt? = null,
)
@Serializable
class PicDownloadExt
@Serializable
class PttDownloadExt
@Serializable
data class VideoDownloadExt(
@ProtoNumber(1) val busiType: UInt?,
@ProtoNumber(2) val sceneType: UInt? = null,
@ProtoNumber(3) val subBusiType: UInt?,
@ProtoNumber(4) val msgCodecConfig: CodecConfigReq,
@ProtoNumber(5) val flag: UInt?,
)
@Serializable
data class CodecConfigReq(
@ProtoNumber(1) val platformChipinfo: String,
@ProtoNumber(2) val osVer: String,
@ProtoNumber(3) val deviceName: String,
)
@Serializable
data class IndexNode(
@ProtoNumber(1) val fileInfo: FileInfo,
@ProtoNumber(2) val fileUuid: String,
@ProtoNumber(3) val storeId: UInt, // 0为旧服务器 1为nt服务器
@ProtoNumber(4) val uploadTime: ULong,
@ProtoNumber(5) val ttl: ULong,
@ProtoNumber(6) val subType: UInt,
@ProtoNumber(7) val storeAppId: UInt? = null
)
@Serializable
data class MultiMediaReqHead(
@ProtoNumber(1) val commonHead: CommonHead,
@ProtoNumber(2) val sceneInfo: SceneInfo,
@ProtoNumber(3) val clientMeta: ClientMeta
)
@Serializable
data class ClientMeta(
@ProtoNumber(1) val agentType: UInt,
)
@Serializable
data class SceneInfo(
@ProtoNumber(101) val requestType: UInt,
@ProtoNumber(102) val businessType: UInt,
@ProtoNumber(103) val appType: UInt? = null,
@ProtoNumber(200) var sceneType: UInt? = null,
@ProtoNumber(201) var c2c: C2CUserInfo? = null,
@ProtoNumber(202) var grp: GroupUserInfo? = null,
@ProtoNumber(203) var channel: ChannelUserInfo? = null,
@ProtoNumber(205) val byteArr: ByteArray?= null
)
@Serializable
data class ChannelUserInfo(
@ProtoNumber(1) val guildId: ULong,
@ProtoNumber(2) val channelId: ULong,
@ProtoNumber(3) val channelType: UInt,
)
@Serializable
data class GroupUserInfo(
@ProtoNumber(1) val uin: ULong,
)
@Serializable
data class C2CUserInfo(
@ProtoNumber(1) val accountType: UInt,
@ProtoNumber(2) val uid: String,
@ProtoNumber(3) val byteArr: ByteArray? = null
)
@Serializable
data class CommonHead(
@ProtoNumber(1) val requestId: ULong,
@ProtoNumber(2) val cmd: UInt,
@ProtoNumber(3) val msg: String? = null
)

View File

@ -0,0 +1,139 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.oidb.cmd0x11c5
import com.google.protobuf.Internal.EMPTY_BYTE_ARRAY
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class NtV2RichMediaRsp(
@ProtoNumber(1) val head: RspHead,
@ProtoNumber(2) val upload: UploadRsp?,
@ProtoNumber(3) val download: DownloadRsp?,
@ProtoNumber(4) val downloadRkeyRsp: DownloadRkeyRsp?,
@ProtoNumber(5) val delete: DeleteRsp?,
@ProtoNumber(6) val uploadCompleted: UploadCompletedRsp?,
@ProtoNumber(7) val msgInfoAuth: MsgInfoAuthRsp?,
@ProtoNumber(8) val uploadKeyRenew: UploadKeyRenewalRsp?,
@ProtoNumber(9) val downloadSafe: DownloadSafeRsp?,
@ProtoNumber(99) val extension: ByteArray? = null,
): Protobuf<NtV2RichMediaRsp>
@Serializable
class DownloadSafeRsp
@Serializable
data class UploadKeyRenewalRsp(
@ProtoNumber(1) val ukey: String,
@ProtoNumber(2) val ukeyTtlSec: ULong,
)
@Serializable
data class MsgInfoAuthRsp(
@ProtoNumber(1) val authCode: UInt = 0u,
@ProtoNumber(2) val msg: ByteArray = EMPTY_BYTE_ARRAY,
@ProtoNumber(3) val resultTime: ULong = 0u,
)
@Serializable
data class UploadCompletedRsp(
@ProtoNumber(1) val msgSeq: ULong
)
@Serializable
class DeleteRsp
@Serializable
data class DownloadRkeyRsp(
@ProtoNumber(1) val rkeys: List<RKeyInfo>
)
@Serializable
data class RKeyInfo(
@ProtoNumber(1) val rkey: String,
@ProtoNumber(2) val rkeyTtlSec: ULong,
@ProtoNumber(3) val storeId: UInt = 0u,
@ProtoNumber(4) val rkeyCreateTime: UInt?,
@ProtoNumber(4) val type: UInt?,
)
@Serializable
data class DownloadRsp(
@ProtoNumber(1) val rkeyParam: String,
@ProtoNumber(2) val rkeyTtlSec: ULong,
@ProtoNumber(3) val downloadInfo: DownloadInfo?,
@ProtoNumber(4) val rkeyCreateTime: UInt?
)
@Serializable
data class DownloadInfo(
@ProtoNumber(1) val domain: String,
@ProtoNumber(2) val urlPath: String? = null,
@ProtoNumber(3) val httpsPort: Int = Int.MIN_VALUE,
@ProtoNumber(4) val ipv4: List<Ipv4>,
@ProtoNumber(5) val ipv6: List<Ipv6>,
@ProtoNumber(6) val picUrlExtInfo: PicUrlExtInfo?,
@ProtoNumber(7) val videoExtInfo: VideoExtInfo? = null,
)
@Serializable
data class VideoExtInfo(
@ProtoNumber(1) val videoCodecFormat: UInt,
)
@Serializable
data class UploadRsp(
@ProtoNumber(1) val ukey: String,
@ProtoNumber(2) val ukeyTtlSec: ULong,
@ProtoNumber(3) val ipv4: List<Ipv4>,
@ProtoNumber(4) val ipv6: List<Ipv6>,
@ProtoNumber(5) val msgSeq: ULong,
@ProtoNumber(6) val msgInfo: MsgInfo? = null,
@ProtoNumber(7) val ext: List<RichmediaStorageTransInfo>? = null,
@ProtoNumber(8) val compatQMsg: ByteArray? = null,
@ProtoNumber(10) val subFileInfos: List<SubFileInfo>? = null,
)
@Serializable
data class SubFileInfo(
@ProtoNumber(1) val subType: UInt,
@ProtoNumber(2) val ukey: String,
@ProtoNumber(3) val ukeyTTLSec: ULong,
@ProtoNumber(4) val ipv4: List<Ipv4>,
@ProtoNumber(5) val ipv6: List<Ipv6>,
)
@Serializable
data class RichmediaStorageTransInfo(
@ProtoNumber(1) val subType: UInt = UInt.MIN_VALUE,
@ProtoNumber(2) val extType: UInt = UInt.MIN_VALUE,
@ProtoNumber(3) val extValue: ByteArray? = null,
)
@Serializable
data class Ipv4(
@ProtoNumber(1) val outIp: Int = Int.MIN_VALUE,
@ProtoNumber(2) val outPort: Int = Int.MIN_VALUE,
@ProtoNumber(3) val inIp: Int = Int.MIN_VALUE,
@ProtoNumber(4) val inPort: Int = Int.MIN_VALUE,
@ProtoNumber(5) val ipType: Int = Int.MIN_VALUE,
)
@Serializable
data class Ipv6(
@ProtoNumber(1) val outIp: ByteArray? = null,
@ProtoNumber(2) val outPort: Int = Int.MIN_VALUE,
@ProtoNumber(3) val inIp: ByteArray? = null,
@ProtoNumber(4) val inPort: Int = Int.MIN_VALUE,
@ProtoNumber(5) val ipType: Int = Int.MIN_VALUE,
)
@Serializable
data class RspHead(
@ProtoNumber(1) val commonHead: CommonHead,
@ProtoNumber(2) val retCode: UInt = 0u,
@ProtoNumber(3) val msg: String
)

View File

@ -5,6 +5,7 @@ package protobuf.oidb.cmd0x6d7
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
import protobuf.group_file_common.FolderInfo
@Serializable
@ -13,7 +14,7 @@ data class Oidb0x6d7ReqBody(
@ProtoNumber(2) val deleteFolder: DeleteFolderReq? = null,
@ProtoNumber(3) val moveFolder: MoveFolderReq? = null,
@ProtoNumber(4) val renameFolder: RenameFolderReq? = null,
)
): Protobuf<Oidb0x6d7ReqBody>
@Serializable
data class CreateFolderReq(
@ -53,7 +54,7 @@ data class Oidb0x6d7RespBody(
@ProtoNumber(2) val deleteFolder: DeleteFolderResp? = null,
@ProtoNumber(3) val moveFolder: MoveFolderResp? = null,
@ProtoNumber(4) val renameFolder: RenameFolderResp? = null,
)
): Protobuf<Oidb0x6d7RespBody>
@Serializable
data class CreateFolderResp(

View File

@ -2,6 +2,7 @@ package protobuf.oidb.cmd0x9082
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class Oidb0x9082(
@ -11,4 +12,4 @@ data class Oidb0x9082(
@ProtoNumber(5) val flag: UInt = UInt.MIN_VALUE,
@ProtoNumber(6) val u1: UInt = UInt.MIN_VALUE,
@ProtoNumber(7) val u2: UInt = UInt.MIN_VALUE,
)
): Protobuf<Oidb0x9082>

View File

@ -2,11 +2,12 @@ package protobuf.oidb.cmd0xf16
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class Oidb0xf16(
@ProtoNumber(1) var setGroupRemarkReq: SetGroupRemarkReq? = null,
)
): Protobuf<Oidb0xf16>
@Serializable
data class SetGroupRemarkReq(

View File

@ -5,6 +5,7 @@ package protobuf.oidb.cmd0xf88
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class Oidb0xf88Req(
@ -12,12 +13,12 @@ data class Oidb0xf88Req(
@ProtoNumber(2) val memberId: ULong,
@ProtoNumber(3) val tinyId: ULong,
@ProtoNumber(4) val guildId: ULong,
)
): Protobuf<Oidb0xf88Req>
@Serializable
data class Oidb0xf88Rsp(
@ProtoNumber(1) val userInfo: GProUserInfo?
)
): Protobuf<Oidb0xf88Rsp>
@Serializable
data class GProUserInfo(

View File

@ -4,6 +4,7 @@ package protobuf.oidb.cmd0xfc2
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class Oidb0xfc2ReqBody(
@ -16,7 +17,7 @@ data class Oidb0xfc2ReqBody(
@ProtoNumber(300) var msgApplyDownloadReq: Oidb0xfc2MsgApplyDownloadReq? = null,
//@ProtoNumber(400) var msg_apply_preview_req: Any? = null,
//@ProtoNumber(500) var msg_apply_security_strike_req: Any? = null,
)
): Protobuf<Oidb0xfc2ReqBody>
@Serializable
data class Oidb0xfc2RspBody(
@ -27,7 +28,7 @@ data class Oidb0xfc2RspBody(
@ProtoNumber(310) var msgApplyDownloadRsp: Oidb0xfc2MsgApplyDownloadRsp? = null,
//@ProtoNumber(410) var msg_apply_preview_rsp: Any? = null,
//@ProtoNumber(510) var msg_apply_security_strike_rsp: Any? = null,
)
): Protobuf<Oidb0xfc2RspBody>
@Serializable
data class Oidb0xfc2MsgApplyDownloadRsp(

View File

@ -5,17 +5,18 @@ package protobuf.oidb.cmx0xf57
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class Oidb0xf57Req(
@ProtoNumber(1) val filter: Oidb0xf57Filter,
@ProtoNumber(2) val guildInfo: Oidb0xf57GuildInfo,
)
): Protobuf<Oidb0xf57Req>
@Serializable
data class Oidb0xf57Rsp(
@ProtoNumber(1) val metaInfo: Oidb0xf57MetaInfo,
)
): Protobuf<Oidb0xf57Rsp>
@Serializable
data class Oidb0xf57MetaInfo(

View File

@ -2,12 +2,13 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class C2CCommonTipsEvent(
@ProtoNumber(7) val params: List<PokeParam>? = null,
@ProtoNumber(8) val xmlTips: String? = null,
)
): Protobuf<C2CCommonTipsEvent>
@Serializable
data class PokeParam(

View File

@ -2,11 +2,12 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class C2CRecallEvent(
@ProtoNumber(1) val head: C2CRecallHead? = null,
)
): Protobuf<C2CRecallEvent>
@Serializable
data class C2CRecallHead(

View File

@ -2,11 +2,12 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class FriendApplyEvent(
@ProtoNumber(1) val head: FriendApplyHead? = null,
)
): Protobuf<FriendApplyEvent>
@Serializable

View File

@ -2,10 +2,11 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupAdminChangeEvent(
@ProtoNumber(1) val groupCode: Long,
@ProtoNumber(4) val operation: GroupAdminChangedOperation? = null
)
): Protobuf<GroupAdminChangeEvent>

View File

@ -2,10 +2,11 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupApplyEvent(
@ProtoNumber(1) val groupCode: Long = Long.MIN_VALUE,
@ProtoNumber(3) val applierUid: String = "",
@ProtoNumber(5) val applyMsg: String? = null,
)
): Protobuf<GroupApplyEvent>

View File

@ -2,13 +2,14 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupBanEvent(
@ProtoNumber(1) val groupCode: ULong = ULong.MIN_VALUE,
@ProtoNumber(4) val operatorUid: String = "",
@ProtoNumber(5) val target: GroupBanTarget? = null,
)
): Protobuf<GroupBanEvent>
@Serializable
data class GroupBanTarget(

View File

@ -2,6 +2,7 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupCommonTipsEvent(
@ -11,7 +12,7 @@ data class GroupCommonTipsEvent(
@ProtoNumber(26) val baseTips: List<GroupBaseTips>? = null,
@ProtoNumber(33) val essenceMsgInfo: List<EssenceMsgInfo>? = null,
@ProtoNumber(37) val msgSeq: ULong = ULong.MIN_VALUE,
)
): Protobuf<GroupCommonTipsEvent>
@Serializable
data class EssenceMsgInfo(

View File

@ -2,9 +2,10 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupInviteEvent(
@ProtoNumber(1) val groupCode: Long,
@ProtoNumber(5) val inviterUid: String,
)
): Protobuf<GroupInviteEvent>

View File

@ -2,11 +2,12 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupInvitedApplyEvent(
@ProtoNumber(2) val applyInfo: GroupInvitedApplyInfo? = null,
)
): Protobuf<GroupInvitedApplyEvent>
@Serializable
data class GroupInvitedApplyInfo(

View File

@ -2,6 +2,7 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GroupListChangeEvent(
@ -9,4 +10,4 @@ data class GroupListChangeEvent(
@ProtoNumber(3) val memberUid: String = "",
@ProtoNumber(4) val type: Int = Int.MIN_VALUE,
@ProtoNumber(5) val operatorUid: String = "",
)
): Protobuf<GroupListChangeEvent>

View File

@ -2,13 +2,14 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
import protobuf.message.NtMessage
@Serializable
data class MessagePush(
@ProtoNumber(1) val msgBody: NtMessage? = null,
@ProtoNumber(4) val clientInfo: MessagePushClientInfo? = null,
)
): Protobuf<MessagePush>
@Serializable
data class MessagePushClientInfo(

View File

@ -5,6 +5,7 @@ package protobuf.qweb
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class QWebReq(
@ -19,7 +20,7 @@ data class QWebReq(
//@ProtoNumber(9) var Crypto: Any? = null,
@ProtoNumber(10) var extinfo: List<QWebExtInfo>? = null,
//@ProtoNumber(11) var contentType: Any? = null,
)
): Protobuf<QWebReq>
@Serializable
data class QWebExtInfo(
@ -34,4 +35,4 @@ data class QWebRsp(
//@ProtoNumber(3) var errMsg: String? = null,
@ProtoNumber(4) var buffer: ByteArray? = null,
//@ProtoNumber(5) var Extinfo: List<QWebExtInfo>? = null,
)
): Protobuf<QWebRsp>

View File

@ -19,7 +19,7 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.utils.PlatformUtils
@ -28,6 +28,7 @@ import moe.fuqiuluo.shamrock.xposed.helper.internal.DynamicReceiver
import moe.fuqiuluo.shamrock.xposed.helper.internal.IPCRequest
import protobuf.oidb.TrpcOidb
import mqq.app.MobileQQ
import protobuf.auto.toByteArray
import tencent.im.oidb.oidb_sso
import kotlin.coroutines.CoroutineContext
import kotlin.coroutines.resume
@ -127,9 +128,9 @@ internal abstract class BaseSvc {
cmd = cmdId,
service = serviceId,
buffer = buffer,
flag = 0
flag = 1
)
to.putWupBuffer(ProtoBuf.encodeToByteArray(oidb))
to.putWupBuffer(oidb.toByteArray())
to.addAttribute("req_pb_protocol_flag", true)
if (seq != -1) {

View File

@ -1,21 +1,20 @@
package moe.fuqiuluo.qqinterface.servlet
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import protobuf.auto.toByteArray
import protobuf.oidb.cmd0x9082.Oidb0x9082
internal object ChatSvc: BaseSvc() {
fun setGroupMessageCommentFace(peer: Long, msgSeq: ULong, faceIndex: String, isSet: Boolean) {
val serviceId = if (isSet) 1 else 2
sendOidb("OidbSvcTrpcTcp.0x9082_$serviceId", 36994, serviceId, ProtoBuf.encodeToByteArray(
Oidb0x9082(
sendOidb("OidbSvcTrpcTcp.0x9082_$serviceId", 36994, serviceId, Oidb0x9082(
peer = peer.toULong(),
msgSeq = msgSeq,
faceIndex = faceIndex,
flag = 1u,
u1 = 0u,
u2 = 0u
)
))
).toByteArray())
}
}

View File

@ -1,9 +1,6 @@
package moe.fuqiuluo.qqinterface.servlet
import com.tencent.mobileqq.pb.ByteStringMicro
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.structures.*
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
import moe.fuqiuluo.shamrock.helper.Level
@ -12,6 +9,8 @@ import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.symbols.decode
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.oidb.cmd0x6d7.CreateFolderReq
import protobuf.oidb.cmd0x6d7.DeleteFolderReq
import protobuf.oidb.cmd0x6d7.MoveFolderReq
@ -22,24 +21,25 @@ import tencent.im.oidb.cmd0x6d6.oidb_0x6d6
import tencent.im.oidb.cmd0x6d8.oidb_0x6d8
import tencent.im.oidb.oidb_sso
import protobuf.group_file_common.FolderInfo as GroupFileCommonFolderInfo
import protobuf.auto.toByteArray
internal object FileSvc: BaseSvc() {
suspend fun createFileFolder(groupId: String, folderName: String, parentFolderId: String = "/"): Result<GroupFileCommonFolderInfo> {
val data = ProtoBuf.encodeToByteArray(
Oidb0x6d7ReqBody(
val data = Oidb0x6d7ReqBody(
createFolder = CreateFolderReq(
groupCode = groupId.toULong(),
appId = 3u,
parentFolderId = parentFolderId,
folderName = folderName
)
)
)
).toByteArray()
val resultBuffer = sendOidbAW("OidbSvc.0x6d7_0", 1751, 0, data)
?: return Result.failure(Exception("unable to fetch result"))
val oidbPkg = oidb_sso.OIDBSSOPkg()
oidbPkg.mergeFrom(resultBuffer.slice(4))
val rsp = ProtoBuf.decodeFromByteArray<Oidb0x6d7RespBody>(oidbPkg.bytes_bodybuffer.get().toByteArray())
val rsp = oidbPkg.bytes_bodybuffer.get()
.toByteArray()
.decodeProtobuf<Oidb0x6d7RespBody>()
if (rsp.createFolder?.retCode != 0) {
return Result.failure(Exception("unable to create folder: ${rsp.createFolder?.retCode}"))
}
@ -47,52 +47,46 @@ internal object FileSvc: BaseSvc() {
}
suspend fun deleteGroupFolder(groupId: String, folderUid: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_1", 1751, 1, ProtoBuf.encodeToByteArray(
Oidb0x6d7ReqBody(
val buffer = sendOidbAW("OidbSvc.0x6d7_1", 1751, 1, Oidb0x6d7ReqBody(
deleteFolder = DeleteFolderReq(
groupCode = groupId.toULong(),
appId = 3u,
folderId = folderUid
)
)
)) ?: return false
).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = ProtoBuf.decodeFromByteArray<Oidb0x6d7RespBody>(oidbPkg.bytes_bodybuffer.get().toByteArray())
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.deleteFolder?.retCode == 0
}
suspend fun moveGroupFolder(groupId: String, folderUid: String, newParentFolderUid: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_2", 1751, 2, ProtoBuf.encodeToByteArray(
Oidb0x6d7ReqBody(
val buffer = sendOidbAW("OidbSvc.0x6d7_2", 1751, 2, Oidb0x6d7ReqBody(
moveFolder = MoveFolderReq(
groupCode = groupId.toULong(),
appId = 3u,
folderId = folderUid,
parentFolderId = "/"
)
)
)) ?: return false
).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = ProtoBuf.decodeFromByteArray<Oidb0x6d7RespBody>(oidbPkg.bytes_bodybuffer.get().toByteArray())
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.moveFolder?.retCode == 0
}
suspend fun renameFolder(groupId: String, folderUid: String, name: String): Boolean {
val buffer = sendOidbAW("OidbSvc.0x6d7_3", 1751, 3, ProtoBuf.encodeToByteArray(
Oidb0x6d7ReqBody(
val buffer = sendOidbAW("OidbSvc.0x6d7_3", 1751, 3, Oidb0x6d7ReqBody(
renameFolder = RenameFolderReq(
groupCode = groupId.toULong(),
appId = 3u,
folderId = folderUid,
folderName = name
)
)
)) ?: return false
).toByteArray()) ?: return false
val oidbPkg = oidb_sso.OIDBSSOPkg()
oidbPkg.mergeFrom(buffer.slice(4))
val rsp = ProtoBuf.decodeFromByteArray<Oidb0x6d7RespBody>(oidbPkg.bytes_bodybuffer.get().toByteArray())
val rsp = oidbPkg.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0x6d7RespBody>()
return rsp.renameFolder?.retCode == 0
}

View File

@ -7,14 +7,10 @@ import com.tencent.qqnt.kernel.nativeinterface.GProGuildRole
import com.tencent.qqnt.kernel.nativeinterface.GProRoleCreateInfo
import com.tencent.qqnt.kernel.nativeinterface.GProRoleMemberList
import com.tencent.qqnt.kernel.nativeinterface.GProRolePermission
import io.ktor.utils.io.ByteReadChannel
import io.ktor.utils.io.core.readBytes
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.structures.GProChannelInfo
import moe.fuqiuluo.qqinterface.servlet.structures.GetGuildMemberListNextToken
import moe.fuqiuluo.qqinterface.servlet.structures.GuildInfo
@ -24,10 +20,11 @@ import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.EMPTY_BYTE_ARRAY
import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.symbols.decode
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray
import protobuf.guild.GetGuildFeedsReq
import protobuf.guild.GetGuildFeedsRsp
import protobuf.oidb.cmd0xf88.GProFilter
@ -54,33 +51,31 @@ internal object GProSvc: BaseSvc() {
}
suspend fun getGuildInfo(guildId: ULong): Result<Oidb0xf57MetaInfo> {
val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf57_9", 0xf57, 9, ProtoBuf.encodeToByteArray(
Oidb0xf57Req(
val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf57_9", 0xf57, 9, Oidb0xf57Req(
filter = Oidb0xf57Filter(
u1 = Oidb0xf57U1(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u),
u2 = Oidb0xf57U2(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u)
),
guildInfo = Oidb0xf57GuildInfo(guildId = guildId)
)
))
).toByteArray())
val body = oidb_sso.OIDBSSOPkg()
if (respBuffer == null) {
return Result.failure(Exception("unable to send packet"))
}
body.mergeFrom(respBuffer.slice(4))
return runCatching {
ProtoBuf.decodeFromByteArray<Oidb0xf57Rsp>(
body.bytes_bodybuffer.get().toByteArray()
).metaInfo
body.bytes_bodybuffer.get()
.toByteArray()
.decodeProtobuf<Oidb0xf57Rsp>().metaInfo
}
}
suspend fun getGuildFeeds(guildId: ULong, channelId: ULong, startIndex: Int): Result<GetGuildFeedsRsp> {
val buffer = sendBufferAW("QChannelSvr.trpc.qchannel.commreader.ComReader.GetGuildFeeds", true, ProtoBuf.encodeToByteArray(QWebReq(
val buffer = sendBufferAW("QChannelSvr.trpc.qchannel.commreader.ComReader.GetGuildFeeds", true, QWebReq(
seq = 10,
qua = PlatformUtils.getQUA(),
deviceInfo = "i=&imsi=&mac=02:00:00:00:00:00&m=Shamrock&o=114514&a=1919810&sd=0&c64=1&sc=1&p=8000*8000&aid=123456789012345678901234567890abcdef&f=Tencent&mm=5610&cf=1726&cc=8&qimei=&qimei36=&sharpP=1&n=nether_world&support_xsj_live=false&client_mod=concise&timezone=America/La_Paz&material_sdk_version=&vh265=&refreshrate=10086&hwlevel=9&suphdr=1&is_teenager_mod=8&liveH265=&bmst=5&AV1=0",
buffer = ProtoBuf.encodeToByteArray(GetGuildFeedsReq(
buffer = GetGuildFeedsReq(
count = 12,
from = startIndex,
feedAttchInfo = EMPTY_BYTE_ARRAY,
@ -89,18 +84,18 @@ internal object GProSvc: BaseSvc() {
u7 = 0,
u8 = 1,
u9 = EMPTY_BYTE_ARRAY
)),
).toByteArray(),
traceId = app.account + "_0_0",
extinfo = listOf(
QWebExtInfo("fc-appid", "96"),
QWebExtInfo("environment_id", "shamrock"),
QWebExtInfo("tiny_id", getSelfTinyId().toString()),
)
))) ?: return Result.failure(Exception("unable to send packet"))
val webRsp = ProtoBuf.decodeFromByteArray<QWebRsp>(buffer.slice(4))
).toByteArray()) ?: return Result.failure(Exception("unable to send packet"))
val webRsp = buffer.slice(4).decodeProtobuf<QWebRsp>()
if(webRsp.buffer == null) return Result.failure(Exception("server error"))
val wupBuffer = webRsp.buffer!!
val feeds = ProtoBuf.decodeFromByteArray<GetGuildFeedsRsp>(wupBuffer)
val feeds = wupBuffer.decodeProtobuf<GetGuildFeedsRsp>()
return Result.success(feeds)
}
@ -188,23 +183,19 @@ internal object GProSvc: BaseSvc() {
guildId: ULong,
memberTinyId: ULong
): Result<GProUserInfo> {
val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf88_1", 0xf88, 1, ProtoBuf.encodeToByteArray(
Oidb0xf88Req(
val respBuffer = sendOidbAW("OidbSvcTrpcTcp.0xf88_1", 0xf88, 1, Oidb0xf88Req(
filter = GProFilter(1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u, 1u),
memberId = 0uL,
tinyId = memberTinyId,
guildId = guildId
)
))
).toByteArray())
val body = oidb_sso.OIDBSSOPkg()
if (respBuffer == null) {
return Result.failure(Exception("unable to send packet"))
}
body.mergeFrom(respBuffer.slice(4))
return runCatching {
ProtoBuf.decodeFromByteArray<Oidb0xf88Rsp>(
body.bytes_bodybuffer.get().toByteArray()
).userInfo!!
body.bytes_bodybuffer.get().toByteArray().decodeProtobuf<Oidb0xf88Rsp>().userInfo!!
}
}

View File

@ -46,7 +46,7 @@ import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.json.decodeFromStream
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getLongUin
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getUin
import moe.fuqiuluo.qqinterface.servlet.structures.GroupAtAllRemainInfo
@ -78,6 +78,7 @@ import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import protobuf.oidb.cmd0xf16.Oidb0xf16
import protobuf.oidb.cmd0xf16.SetGroupRemarkReq
import mqq.app.MobileQQ
import protobuf.auto.toByteArray
import tencent.im.group.group_member_info
import tencent.im.oidb.cmd0x88d.oidb_0x88d
import tencent.im.oidb.cmd0x899.oidb_0x899
@ -274,15 +275,13 @@ internal object GroupSvc: BaseSvc() {
}
fun modifyGroupRemark(groupId: Long, remark: String): Boolean {
sendOidb("OidbSvc.0xf16_1", 3862, 1, ProtoBuf.encodeToByteArray(
Oidb0xf16(
sendOidb("OidbSvc.0xf16_1", 3862, 1, Oidb0xf16(
setGroupRemarkReq = SetGroupRemarkReq(
groupCode = groupId.toULong(),
groupUin = groupCode2GroupUin(groupId).toULong(),
groupRemark = remark
)
)
))
).toByteArray())
return true
}

View File

@ -11,10 +11,8 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.delay
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.msg.messageelement.toSegments
import moe.fuqiuluo.qqinterface.servlet.msg.toListMap
import moe.fuqiuluo.shamrock.helper.ContactHelper
@ -28,6 +26,9 @@ import moe.fuqiuluo.shamrock.tools.*
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.xposed.helper.NTServiceFetcher
import moe.fuqiuluo.shamrock.xposed.helper.msgService
import moe.fuqiuluo.symbols.decode
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray
import protobuf.message.longmsg.*
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
@ -239,14 +240,14 @@ internal object MsgSvc : BaseSvc() {
)
)
)
LogCenter.log(ProtoBuf.encodeToByteArray(payload).toHexString(), Level.DEBUG)
LogCenter.log(payload.toByteArray().toHexString(), Level.DEBUG)
val req = LongMsgReq(
sendInfo = SendLongMsgInfo(
type = if (groupUin == null) 1 else 3,
uid = LongMsgUid(groupUin ?: uid),
groupUin = groupUin?.toInt(),
payload = DeflateTools.gzip(ProtoBuf.encodeToByteArray(payload))
payload = DeflateTools.gzip(payload.toByteArray())
),
setting = LongMsgSettings(
field1 = 4,
@ -258,9 +259,9 @@ internal object MsgSvc : BaseSvc() {
val buffer = sendBufferAW(
"trpc.group.long_msg_interface.MsgService.SsoSendLongMsg",
true,
ProtoBuf.encodeToByteArray(req)
req.toByteArray()
) ?: return Result.failure(Exception("unable to upload multi message"))
val rsp = ProtoBuf.decodeFromByteArray<LongMsgRsp>(buffer.slice(4))
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
return rsp.sendResult?.resId?.let { Result.success(it) }
?: Result.failure(Exception("unable to upload multi message"))
}
@ -282,14 +283,14 @@ internal object MsgSvc : BaseSvc() {
val buffer = sendBufferAW(
"trpc.group.long_msg_interface.MsgService.SsoRecvLongMsg",
true,
ProtoBuf.encodeToByteArray(req)
req.toByteArray()
) ?: return Result.failure(Exception("unable to get multi message"))
val rsp = ProtoBuf.decodeFromByteArray<LongMsgRsp>(buffer.slice(4))
val rsp = buffer.slice(4).decodeProtobuf<LongMsgRsp>()
val zippedPayload = DeflateTools.ungzip(
rsp.recvResult?.payload ?: return Result.failure(Exception("unable to get multi message"))
)
LogCenter.log(zippedPayload.toHexString(), Level.DEBUG)
val payload = ProtoBuf.decodeFromByteArray<LongMsgPayload>(zippedPayload)
val payload = zippedPayload.decodeProtobuf<LongMsgPayload>()
payload.action?.forEach {
if (it.command == "MultiMsg") {
return Result.success(it.data?.body?.map { msg ->

View File

@ -10,7 +10,7 @@ import io.ktor.utils.io.core.writeInt
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.withTimeoutOrNull
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
import moe.fuqiuluo.shamrock.tools.broadcast
@ -24,6 +24,7 @@ import protobuf.message.MessageHead
import protobuf.message.MessageBody
import protobuf.push.MessagePush
import mqq.app.MobileQQ
import protobuf.auto.toByteArray
import kotlin.coroutines.resume
internal object PacketSvc: BaseSvc() {
@ -77,7 +78,7 @@ internal object PacketSvc: BaseSvc() {
)
)
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, ProtoBuf.encodeToByteArray(msgPush))
fakeReceive("trpc.msg.olpush.OlPushService.MsgPush", 10000, msgPush.toByteArray())
return withTimeoutOrNull(5000L) {
suspendCancellableCoroutine {
AioListener.registerTemporaryMsgListener(msgSeq) {

View File

@ -15,7 +15,7 @@ import kotlinx.io.core.readBytes
import kotlinx.io.core.writeFully
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
@ -39,6 +39,7 @@ import mqq.manager.TicketManager
import oicq.wlogin_sdk.request.Ticket
import oicq.wlogin_sdk.request.WtTicketPromise
import oicq.wlogin_sdk.tools.ErrMsg
import protobuf.auto.toByteArray
import java.io.ByteArrayOutputStream
import java.io.File
import java.nio.ByteBuffer
@ -329,9 +330,9 @@ internal object QFavSvc: BaseSvc() {
}
val pSKey = getWeiYunPSKey()
httpNetReq.mHttpMethod = HttpNetReq.HTTP_POST
httpNetReq.mSendData = DeflateTools.gzip(packData(packHead(cmd, pSKey), ProtoBuf.encodeToByteArray(
WeiyunComm(req = req)
)))
httpNetReq.mSendData = DeflateTools.gzip(packData(packHead(cmd, pSKey), WeiyunComm(
req = req
).toByteArray()))
httpNetReq.mOutStream = outputStream
httpNetReq.mStartDownOffset = 0L
httpNetReq.mReqProperties["Shamrock"] = "true"
@ -351,8 +352,7 @@ internal object QFavSvc: BaseSvc() {
}
private fun packHead(cmd: Int, pskey: String): ByteArray {
return ProtoBuf.encodeToByteArray(
WeiyunMsgHead(
return WeiyunMsgHead(
uin = app.longAccountUin.toULong(),
seq = seq++.toUInt(),
type = 1u,
@ -364,8 +364,7 @@ internal object QFavSvc: BaseSvc() {
key = pskey.toByteArray(),
majorVersion = MAJOR_VERSION.toUInt(),
minorVersion = MINOR_VERSION.toUInt(),
)
)
).toByteArray()
}
private fun packData(head: ByteArray, body: ByteArray): ByteArray {

View File

@ -26,4 +26,12 @@ sealed class ArkAppInfo(
signature = "7194d531cbe7960a22007b9f6bdaa38b",
miniAppId = 1109937557
)
data object Docs: ArkAppInfo(
appId = 0,
version = "0.0.0",
packageName = "",
signature = "f3da3147654d9a21f3237b88f20dce9c",
miniAppId = 1108338344
)
}

View File

@ -53,6 +53,7 @@ internal object ArkMsgSvc: BaseSvc() {
sendOidb("OidbSvc.0xb77_9", 0xb77, 9, req.toByteArray())
}
/*
suspend fun tryShareJsonMessage(
jsonString: String,
arkAppInfo: ArkAppInfo = ArkAppInfo.DanMaKu,
@ -96,5 +97,5 @@ internal object ArkMsgSvc: BaseSvc() {
}
} ?: return Result.failure(Exception("unable to sign json"))
return Result.success(signedJson)
}
}*/
}

View File

@ -0,0 +1,9 @@
package moe.fuqiuluo.qqinterface.servlet.ark
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
internal object LightAppSvc: BaseSvc() {
suspend fun adaptShare() {
}
}

View File

@ -184,19 +184,20 @@ internal object MsgElementConverter {
element: MsgElement
): MessageSegment {
val image = element.picElement
val md5 = image.md5HexStr ?: image.fileName
val md5 = (image.md5HexStr ?: image.fileName
.replace("{", "")
.replace("}", "")
.replace("-", "").split(".")[0]
.replace("-", "").split(".")[0])
.uppercase()
ImageDB.getInstance().imageMappingDao().insert(
ImageMapping(md5.uppercase(), chatType, image.fileSize)
ImageMapping(md5, chatType, image.fileSize)
)
//LogCenter.log(image.toString())
val originalUrl = image.originImageUrl ?: ""
//LogCenter.log({ "receive image: $image" }, Level.DEBUG)
LogCenter.log({ "receive image: $image" }, Level.DEBUG)
return MessageSegment(
type = "image",
@ -204,12 +205,37 @@ internal object MsgElementConverter {
"file" to md5,
"url" to when (chatType) {
MsgConstant.KCHATTYPEDISC, MsgConstant.KCHATTYPEGROUP -> RichProtoSvc.getGroupPicDownUrl(
originalUrl,
md5
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId
)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(originalUrl, md5)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(originalUrl, md5)
MsgConstant.KCHATTYPEC2C -> RichProtoSvc.getC2CPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId,
)
MsgConstant.KCHATTYPEGUILD -> RichProtoSvc.getGuildPicDownUrl(
originalUrl = originalUrl,
md5 = md5,
fileId = image.fileUuid,
width = image.picWidth.toUInt(),
height = image.picHeight.toUInt(),
sha = "",
fileSize = image.fileSize.toULong(),
peer = peerId,
subPeer = subPeer
)
else -> throw UnsupportedOperationException("Not supported chat type: $chatType")
},
"subType" to image.picSubType,

View File

@ -2,6 +2,7 @@ package moe.fuqiuluo.qqinterface.servlet.structures
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import moe.fuqiuluo.symbols.Protobuf
@Serializable
data class GuildInfo(
@ -59,7 +60,7 @@ data class GetGuildMemberListNextToken(
@SerialName("role_index") val roleIndex: Long,
@SerialName("seq") val seq: Int,
@SerialName("finish") val finish: Boolean
)
): Protobuf<GetGuildMemberListNextToken>
@Serializable
data class GuildMemberInfo(

View File

@ -14,7 +14,6 @@ import kotlinx.coroutines.withTimeoutOrNull
import moe.fuqiuluo.shamrock.utils.MD5
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import mqq.app.AppRuntime
import mqq.app.MobileQQ
import java.io.File
import kotlin.coroutines.resume
import kotlin.math.abs
@ -162,6 +161,17 @@ internal abstract class FileTransfer {
const val BUSI_TYPE_VIDEO_EMOTICON_PIC = 1022
const val BUSI_TYPE_VIDEO_EMOTICON_VIDEO = 1021
const val TRANSFILE_TYPE_PIC = 1
const val TRANSFILE_TYPE_PIC_EMO = 65538
const val TRANSFILE_TYPE_PIC_THUMB = 65537
const val TRANSFILE_TYPE_PISMA = 49
const val TRANSFILE_TYPE_RAWPIC = 131075
const val TRANSFILE_TYPE_PROFILE_COVER = 35
const val TRANSFILE_TYPE_PTT = 2
const val TRANSFILE_TYPE_PTT_SLICE_TO_TEXT = 327696
const val TRANSFILE_TYPE_QQHEAD_PIC = 131074
internal fun createMessageUniseq(time: Long = System.currentTimeMillis()): Long {
var uniseq = (time / 1000).toInt().toLong()
uniseq = uniseq shl 32 or abs(Random.nextInt()).toLong()

View File

@ -6,12 +6,11 @@ import com.tencent.mobileqq.transfile.FileMsg
import com.tencent.mobileqq.transfile.api.IProtoReqManager
import com.tencent.mobileqq.transfile.protohandler.RichProto
import com.tencent.mobileqq.transfile.protohandler.RichProtoProc
import kotlinx.atomicfu.atomic
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.helper.ContactHelper
import moe.fuqiuluo.shamrock.helper.Level
import moe.fuqiuluo.shamrock.helper.LogCenter
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
@ -19,7 +18,26 @@ import moe.fuqiuluo.shamrock.tools.slice
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.shamrock.utils.PlatformUtils
import moe.fuqiuluo.shamrock.xposed.helper.AppRuntimeFetcher
import moe.fuqiuluo.symbols.decodeProtobuf
import mqq.app.MobileQQ
import protobuf.auto.toByteArray
import protobuf.oidb.TrpcOidb
import protobuf.oidb.cmd0x11c5.C2CUserInfo
import protobuf.oidb.cmd0x11c5.ChannelUserInfo
import protobuf.oidb.cmd0x11c5.ClientMeta
import protobuf.oidb.cmd0x11c5.CodecConfigReq
import protobuf.oidb.cmd0x11c5.CommonHead
import protobuf.oidb.cmd0x11c5.DownloadExt
import protobuf.oidb.cmd0x11c5.DownloadReq
import protobuf.oidb.cmd0x11c5.FileInfo
import protobuf.oidb.cmd0x11c5.FileType
import protobuf.oidb.cmd0x11c5.GroupUserInfo
import protobuf.oidb.cmd0x11c5.IndexNode
import protobuf.oidb.cmd0x11c5.MultiMediaReqHead
import protobuf.oidb.cmd0x11c5.NtV2RichMediaReq
import protobuf.oidb.cmd0x11c5.NtV2RichMediaRsp
import protobuf.oidb.cmd0x11c5.SceneInfo
import protobuf.oidb.cmd0x11c5.VideoDownloadExt
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ChannelInfo
import protobuf.oidb.cmd0xfc2.Oidb0xfc2MsgApplyDownloadReq
import protobuf.oidb.cmd0xfc2.Oidb0xfc2ReqBody
@ -31,26 +49,14 @@ import tencent.im.oidb.oidb_sso
import kotlin.coroutines.resume
private const val GPRO_PIC = "gchat.qpic.cn"
private const val GPRO_PIC_NT = "multimedia.nt.qq.com.cn"
private const val MULTIMEDIA_DOMAIN = "multimedia.nt.qq.com.cn"
private const val C2C_PIC = "c2cpicdw.qpic.cn"
internal object RichProtoSvc: BaseSvc() {
var multiMediaRKey = "CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
/*@Deprecated("Use RichProtoSvc.getQQDns instead", ReplaceWith("getQQDns(domain)"))
fun getQQDns(domain: String) {
val bundle = Bundle()
bundle.putString("domain", "xxx")
bundle.putInt("businessType", 1)
val result = BinderMethodProxy
.callServer(QIPCClientHelper.getInstance().client, "InnerDnsModule", "reqDomain2IpList", bundle)
if (result.isSuccess) {
val ipList: ArrayList<IpData> = result.data.getParcelableArrayList("ip")!!
}
}*/
private val requestId = atomic(2L)
suspend fun getGuildFileDownUrl(peerId: String, channelId: String, fileId: String, bizId: Int): String {
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, ProtoBuf.encodeToByteArray(
Oidb0xfc2ReqBody(
val buffer = sendOidbAW("OidbSvcTrpcTcp.0xfc2_0", 4034, 0, Oidb0xfc2ReqBody(
msgCmd = 1200,
msgBusType = 4202,
msgChannelInfo = Oidb0xfc2ChannelInfo(
@ -62,14 +68,16 @@ internal object RichProtoSvc: BaseSvc() {
fieldId = fileId,
supportEncrypt = 0
)
)
)) ?: return ""
).toByteArray()) ?: return ""
val body = oidb_sso.OIDBSSOPkg()
body.mergeFrom(buffer.slice(4))
ProtoBuf.decodeFromByteArray<Oidb0xfc2RspBody>(body.bytes_bodybuffer.get().toByteArray()).msgApplyDownloadRsp?.let {
it.msgDownloadInfo?.let {
return "https://${it.downloadDomain}${it.downloadUrl}&fname=$fileId&isthumb=0"
}
body.bytes_bodybuffer
.get().toByteArray()
.decodeProtobuf<Oidb0xfc2RspBody>()
.msgApplyDownloadRsp?.let {
it.msgDownloadInfo?.let {
return "https://${it.downloadDomain}${it.downloadUrl}&fname=$fileId&isthumb=0"
}
}
return ""
}
@ -158,51 +166,194 @@ internal object RichProtoSvc: BaseSvc() {
}
}
fun getGroupPicDownUrl(
suspend fun getGroupPicDownUrl(
originalUrl: String,
md5: String,
peer: String = "",
fileId: String = "",
sha: String = "",
fileSize: ULong = 0uL,
width: UInt = 0u,
height: UInt = 0u
): String {
val isNtServer = originalUrl.startsWith("/download")
val domain = if (isNtServer) GPRO_PIC_NT else GPRO_PIC
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else GPRO_PIC
if (originalUrl.isNotEmpty()) {
if (isNtServer && !originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
getNtPicRKey(
fileId = fileId,
md5 = md5,
sha = sha,
fileSize = fileSize,
width = width,
height = height
) {
sceneType = 2u
grp = GroupUserInfo(peer.toULong())
}.onSuccess {
return "https://$domain$originalUrl$it"
}.onFailure {
LogCenter.log("getGroupPicDownUrl: ${it.stackTraceToString()}", Level.WARN)
}
}
return "https://$domain$originalUrl"
}
return "https://$domain/gchatpic_new/0/0-0-${md5.uppercase()}/0?term=2"
}
fun getC2CPicDownUrl(
suspend fun getC2CPicDownUrl(
originalUrl: String,
md5: String
md5: String,
peer: String = "",
fileId: String = "",
sha: String = "",
fileSize: ULong = 0uL,
width: UInt = 0u,
height: UInt = 0u
): String {
val isNtServer = originalUrl.startsWith("/download")
val domain = if (isNtServer) GPRO_PIC_NT else C2C_PIC
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else C2C_PIC
if (originalUrl.isNotEmpty()) {
if (fileId.isNotEmpty()) getNtPicRKey(
fileId = fileId,
md5 = md5,
sha = sha,
fileSize = fileSize,
width = width,
height = height
) {
sceneType = 1u
c2c = C2CUserInfo(
accountType = 2u,
uid = ContactHelper.getUidByUinAsync(peer.toLong())
)
}.onSuccess {
if (isNtServer && !originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl$it"
}
}.onFailure {
LogCenter.log("getC2CPicDownUrl: ${it.stackTraceToString()}", Level.WARN)
}
if (isNtServer && !originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
return "https://$domain$originalUrl&rkey="
}
return "https://$domain$originalUrl"
}
return "https://$$domain/offpic_new/0/123-0-${md5.uppercase()}/0?term=2"
return "https://$$domain/offpic_new/0/123-0-${md5}/0?term=2"
}
fun getGuildPicDownUrl(
suspend fun getGuildPicDownUrl(
originalUrl: String,
md5: String
md5: String,
peer: String = "",
subPeer: String = "",
fileId: String = "",
sha: String = "",
fileSize: ULong = 0uL,
width: UInt = 0u,
height: UInt = 0u
): String {
val isNtServer = originalUrl.startsWith("/download")
val domain = if (isNtServer) GPRO_PIC_NT else GPRO_PIC
val domain = if (isNtServer) MULTIMEDIA_DOMAIN else GPRO_PIC
if (originalUrl.isNotEmpty()) {
if (isNtServer && !originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
getNtPicRKey(
fileId = fileId,
md5 = md5,
sha = sha,
fileSize = fileSize,
width = width,
height = height
) {
sceneType = 3u
channel = ChannelUserInfo(peer.toULong(), subPeer.toULong(), 1u)
}.onSuccess {
return "https://$domain$originalUrl$it"
}.onFailure {
LogCenter.log("getGuildPicDownUrl: ${it.stackTraceToString()}", Level.WARN)
}
return "https://$domain$originalUrl&rkey="
}
return "https://$domain$originalUrl"
}
return "https://$domain/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"
}
suspend fun getNtPicRKey(
fileId: String,
md5: String,
sha: String,
fileSize: ULong,
width: UInt,
height: UInt,
sceneBuilder: suspend SceneInfo.() -> Unit
): Result<String> {
runCatching {
val req = run {
NtV2RichMediaReq(
head = MultiMediaReqHead(
commonHead = CommonHead(
requestId = requestId.incrementAndGet().toULong(),
cmd = 200u
),
sceneInfo = SceneInfo(
requestType = 2u,
businessType = 1u,
).apply {
sceneBuilder()
},
clientMeta = ClientMeta(2u)
),
download = DownloadReq(
IndexNode(
FileInfo(
fileSize = fileSize,
md5 = md5.lowercase(),
sha1 = sha.lowercase(),
name = "${md5}.jpg",
fileType = FileType(
fileType = 1u,
picFormat = 1000u,
videoFormat = 0u,
voiceFormat = 0u
),
width = width,
height = height,
time = 0u,
original = 1u
),
fileUuid = fileId,
storeId = 1u,
uploadTime = 0u,
ttl = 0u,
subType = 0u,
storeAppId = 0u
),
DownloadExt(
video = VideoDownloadExt(
busiType = 0u,
subBusiType = 0u,
msgCodecConfig = CodecConfigReq(
platformChipinfo = "",
osVer = "",
deviceName = ""
),
flag = 1u
)
)
)
)
}.toByteArray()
val buffer = sendOidbAW("OidbSvcTrpcTcp.0x11c5_200", 4549, 200, req, true)?.slice(4)
buffer?.decodeProtobuf<TrpcOidb>()?.buffer?.decodeProtobuf<NtV2RichMediaRsp>()?.download?.rkeyParam?.let {
return Result.success(it)
}
}.onFailure {
return Result.failure(it)
}
return Result.failure(Exception("unable to get c2c nt pic"))
}
suspend fun getC2CVideoDownUrl(
peerId: String,
md5: ByteArray,

View File

@ -7,7 +7,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
@ -16,6 +16,7 @@ import moe.fuqiuluo.shamrock.utils.CryptTools
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.shamrock.utils.FileUtils
import moe.fuqiuluo.symbols.OneBotHandler
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.fav.WeiyunComm
@OneBotHandler("fav.add_image_msg")
@ -74,7 +75,7 @@ internal object FavAddImageMsg: IActionHandler() {
readPacket.readFully(it, 0, it.size)
}
val resp = ProtoBuf.decodeFromByteArray<WeiyunComm>(data)
val resp = data.decodeProtobuf<WeiyunComm>()
.resp!!.fastUploadResourceResp!!.picResultList!!.first()
val picInfo = resp.picInfo!!
picUrl = picInfo.uri
@ -133,7 +134,7 @@ internal object FavAddImageMsg: IActionHandler() {
val data = ByteArray(dataLength).also {
readPacket.readFully(it, 0, it.size)
}
val resp = ProtoBuf.decodeFromByteArray<WeiyunComm>(data).resp!!
val resp = data.decodeProtobuf<WeiyunComm>().resp!!
itemId = resp.addRichMediaResp!!.cid
}

View File

@ -6,13 +6,14 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.symbols.OneBotHandler
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.fav.WeiyunComm
@OneBotHandler("fav.add_text_msg")
@ -55,7 +56,7 @@ internal object FavAddTextMsg: IActionHandler() {
val data = ByteArray(dataLength).also {
readPacket.readFully(it, 0, it.size)
}
val resp = ProtoBuf.decodeFromByteArray<WeiyunComm>(data).resp!!.addRichMediaResp!!
val resp = data.decodeProtobuf<WeiyunComm>().resp!!.addRichMediaResp!!
ok(data = QFavItem(resp.cid), echo)
} else {
logic(it.mErrDesc, echo)

View File

@ -9,13 +9,14 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.symbols.OneBotHandler
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.fav.WeiyunComm
@OneBotHandler("fav.get_item_content")
@ -47,7 +48,7 @@ internal object FavGetItemContent: IActionHandler() {
readPacket.readFully(it, 0, it.size)
}
val resp = ProtoBuf.decodeFromByteArray<WeiyunComm>(data).resp!!
val resp = data.decodeProtobuf<WeiyunComm>().resp!!
return ok(ItemContent(
resp.getFavContentResp!!.content!!.joinToString("") {
String(it.richMedia!!.rawData!!)

View File

@ -6,13 +6,14 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.QFavSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.utils.DeflateTools
import moe.fuqiuluo.symbols.OneBotHandler
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.fav.WeiyunComm
@OneBotHandler("fav.get_item_list")
@ -55,7 +56,7 @@ internal object FavGetItemList: IActionHandler() {
val data = ByteArray(dataLength).also {
readPacket.readFully(it, 0, it.size)
}
val resp = ProtoBuf.decodeFromByteArray<WeiyunComm>(data).resp!!.getFavListResp!!
val resp = data.decodeProtobuf<WeiyunComm>().resp!!.getFavListResp!!
val itemList = arrayListOf<Item>()
val rawItemList = resp.collections!!

View File

@ -8,7 +8,7 @@ import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.GProSvc
import moe.fuqiuluo.qqinterface.servlet.structures.GetGuildMemberListNextToken
import moe.fuqiuluo.qqinterface.servlet.structures.GuildMemberInfo
@ -19,6 +19,8 @@ import moe.fuqiuluo.shamrock.tools.EmptyJsonString
import moe.fuqiuluo.shamrock.tools.hex2ByteArray
import moe.fuqiuluo.shamrock.tools.toHexString
import moe.fuqiuluo.symbols.OneBotHandler
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.auto.toByteArray
@OneBotHandler("get_guild_member_list")
internal object GetGuildMemberList: IActionHandler() {
@ -29,7 +31,7 @@ internal object GetGuildMemberList: IActionHandler() {
}
suspend operator fun invoke(guildId: ULong, all: Boolean, nextTokenStr: String, echo: JsonElement = EmptyJsonString): String {
val curNextToken = if (nextTokenStr.isEmpty()) null else ProtoBuf.decodeFromByteArray<GetGuildMemberListNextToken>(nextTokenStr.hex2ByteArray())
val curNextToken = if (nextTokenStr.isEmpty()) null else nextTokenStr.hex2ByteArray().decodeProtobuf<GetGuildMemberListNextToken>()
val result = GProSvc.getGuildMemberList(
guildId = guildId,
fetchAll = all,
@ -62,7 +64,7 @@ internal object GetGuildMemberList: IActionHandler() {
return ok(GetGuildMemberListResult(
members = members,
finish = nextToken.finish,
nextToken = ProtoBuf.encodeToByteArray(nextToken).toHexString(),
nextToken = nextToken.toByteArray().toHexString(),
), echo)
}

View File

@ -18,7 +18,7 @@ internal object GetImage: IActionHandler() {
return invoke(file, echo)
}
operator fun invoke(file: String, echo: JsonElement = EmptyJsonString): String {
suspend operator fun invoke(file: String, echo: JsonElement = EmptyJsonString): String {
val fileMd5 = file
.replace("{", "")
.replace("}", "")

View File

@ -2,11 +2,12 @@ package moe.fuqiuluo.shamrock.remote.action.handlers
import kotlinx.atomicfu.atomic
import kotlinx.serialization.encodeToByteArray
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.BaseSvc
import moe.fuqiuluo.shamrock.remote.action.ActionSession
import moe.fuqiuluo.shamrock.remote.action.IActionHandler
import moe.fuqiuluo.symbols.OneBotHandler
import protobuf.auto.toByteArray
import protobuf.msg.C2C
import protobuf.msg.ContentHead
import protobuf.msg.Elem
@ -47,7 +48,7 @@ internal object SendMsgByResid: IActionHandler() {
msgRand = Random.nextUInt(),
msgVia = 0u
)
BaseSvc.sendBufferAW("MessageSvc.PbSendMsg", true, ProtoBuf.encodeToByteArray(req))
BaseSvc.sendBufferAW("MessageSvc.PbSendMsg", true, req.toByteArray())
return ok("ok", session.echo)
}
}

View File

@ -13,7 +13,7 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.decodeFromByteArray
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonElement
import kotlinx.serialization.protobuf.ProtoBuf
import moe.fuqiuluo.qqinterface.servlet.FriendSvc.requestFriendSystemMsgNew
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.GroupSvc.requestGroupSystemMsgNew
@ -31,6 +31,8 @@ import moe.fuqiuluo.shamrock.tools.asJsonObject
import moe.fuqiuluo.shamrock.tools.asString
import moe.fuqiuluo.shamrock.tools.readBuf32Long
import moe.fuqiuluo.shamrock.xposed.helper.PacketHandler
import moe.fuqiuluo.symbols.decode
import moe.fuqiuluo.symbols.decodeProtobuf
import protobuf.message.MessageContent
import protobuf.message.MessageHead
import protobuf.message.MessageBody
@ -56,7 +58,7 @@ internal object PrimitiveListener {
PacketHandler.register("trpc.msg.olpush.OlPushService.MsgPush") { _, buffer ->
GlobalScope.launch {
try {
val push = ProtoBuf.decodeFromByteArray<MessagePush>(buffer.slice(4))
val push = buffer.slice(4).decodeProtobuf<MessagePush>()
onMsgPush(push)
} catch (e: Exception) {
LogCenter.log(e.stackTraceToString(), Level.WARN)
@ -109,11 +111,11 @@ internal object PrimitiveListener {
}
private fun onGroupMessage(msgTime: Long, body: MessageBody) {
runCatching {
/*runCatching {
body.rich?.elements?.filter {
it.comm != null && it.comm!!.type == 48
}?.map {
ProtoBuf.decodeFromByteArray<RichMediaForPicData>(it.comm!!.data!!)
it.comm!!.data!!.decodeProtobuf<RichMediaForPicData>()
}?.forEach {
it.display?.show?.download?.url?.let {
RKEY_PATTERN.matcher(it).takeIf {
@ -124,12 +126,11 @@ internal object PrimitiveListener {
}
}
}
}
}*/
}
private suspend fun onC2CPoke(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<C2CCommonTipsEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<C2CCommonTipsEvent>()
if (event.params == null) return
val params = event.params!!.associate {
@ -156,7 +157,7 @@ internal object PrimitiveListener {
clientInfo: MessagePushClientInfo,
richMsg: MessageBody
) {
val event = ProtoBuf.decodeFromByteArray<FriendApplyEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<FriendApplyEvent>()
if (event.head == null) return
val head = event.head!!
val applierUid = head.applierUid
@ -233,15 +234,15 @@ internal object PrimitiveListener {
private suspend fun onGroupUniqueTitleChange(msgTime: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
richMsg.rawBuffer!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(richMsg.rawBuffer!!)
readPacket.readBuf32Long()
readPacket.discardExact(1)
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
})
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.uniqueTitleChangeDetail!!.first()
@ -279,15 +280,15 @@ internal object PrimitiveListener {
) {
if (clientInfo == null) return
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
richMsg.rawBuffer!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(richMsg.rawBuffer!!)
readPacket.readBuf32Long()
readPacket.discardExact(1)
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
})
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.essenceMsgInfo!!.first()
@ -326,15 +327,15 @@ internal object PrimitiveListener {
private suspend fun onGroupPokeAndGroupSign(time: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
richMsg.rawBuffer!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(richMsg.rawBuffer!!)
readPacket.discardExact(4)
readPacket.discardExact(1)
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
})
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupId = event.groupCode.toLong()
val detail = event.baseTips!!.first()
@ -379,7 +380,7 @@ internal object PrimitiveListener {
}
private suspend fun onC2CRecall(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<C2CRecallEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<C2CRecallEvent>()
val head = event.head!!
val operationUid = head.operator!!
@ -404,7 +405,7 @@ internal object PrimitiveListener {
}
private suspend fun onGroupMemIncreased(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupListChangeEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupListChangeEvent>()
val groupCode = event.groupCode
val targetUid = event.memberUid
val type = event.type
@ -434,7 +435,7 @@ internal object PrimitiveListener {
}
private suspend fun onGroupMemberDecreased(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupListChangeEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupListChangeEvent>()
val groupCode = event.groupCode
val targetUid = event.memberUid
val type = event.type
@ -467,7 +468,7 @@ internal object PrimitiveListener {
}
private suspend fun onGroupAdminChange(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupAdminChangeEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupAdminChangeEvent>()
val groupCode = event.groupCode
if (event.operation == null) return
val operation = event.operation!!
@ -494,7 +495,7 @@ internal object PrimitiveListener {
}
private suspend fun onGroupBan(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupBanEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupBanEvent>()
val groupCode = event.groupCode.toLong()
val operatorUid = event.operatorUid
val wholeBan = event.target?.target?.targetUid == null
@ -520,14 +521,14 @@ internal object PrimitiveListener {
private suspend fun onGroupRecall(time: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
richMsg.rawBuffer!!.decodeProtobuf<GroupCommonTipsEvent>()
}.getOrElse {
val readPacket = ByteReadPacket(richMsg.rawBuffer!!)
readPacket.discardExact(4)
readPacket.discardExact(1)
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.readBytes(readPacket.readShort().toInt()).also {
readPacket.release()
})
}.decodeProtobuf<GroupCommonTipsEvent>()
}
val groupCode = event.groupCode.toLong()
val detail = event.recallDetails!!
@ -555,7 +556,7 @@ internal object PrimitiveListener {
private suspend fun onGroupApply(time: Long, contentHead: MessageContent, richMsg: MessageBody) {
when (contentHead.msgType) {
84 -> {
val event = ProtoBuf.decodeFromByteArray<GroupApplyEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupApplyEvent>()
val groupCode = event.groupCode
val applierUid = event.applierUid
val reason = event.applyMsg ?: ""
@ -587,7 +588,7 @@ internal object PrimitiveListener {
}
}
528 -> {
val event = ProtoBuf.decodeFromByteArray<GroupInvitedApplyEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupInvitedApplyEvent>()
val groupCode = event.applyInfo?.groupCode ?: return
val applierUid = event.applyInfo?.applierUid ?: return
var applier = ContactHelper.getUinByUidAsync(applierUid).toLong()
@ -624,7 +625,7 @@ internal object PrimitiveListener {
}
private suspend fun onInviteGroup(time: Long, msgHead: MessageHead, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupInviteEvent>(richMsg.rawBuffer!!)
val event = richMsg.rawBuffer!!.decodeProtobuf<GroupInviteEvent>()
val groupCode = event.groupCode
val invitorUid = event.inviterUid
val invitor = ContactHelper.getUinByUidAsync(invitorUid).toLong()