Shamrock: fix #236

This commit is contained in:
白池 2024-02-18 07:37:47 +08:00
parent 2c8094c8c8
commit 8e6c167987
16 changed files with 237 additions and 93 deletions

View File

@ -5,53 +5,13 @@ import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class MessageBody(
@ProtoNumber(1) val msgHead: MessageHead? = null,
@ProtoNumber(2) val contentHead: MessageContentHead? = null,
@ProtoNumber(3) val richMsg: RichMessage? = null,
)
@Serializable
data class RichMessage(
@ProtoNumber(1) val elements: MessageElementList? = null,
@ProtoNumber(2) val rawBuffer: ByteArray? = null,
)
@Serializable
data class MessageElementList(
@ProtoNumber(1) val font: Font? = null,
@ProtoNumber(2) val elements: List<MessageElement>? = null
)
@Serializable
data class MessageElement(
@ProtoNumber(51) val json: JsonElement? = null,
)
@Serializable
data class JsonElement(
@ProtoNumber(1) val data: ByteArray? = null,
)
@Serializable
data class MessageHead(
@ProtoNumber(1) val peer: Long = Long.MIN_VALUE,
@ProtoNumber(2) val peerUid: String? = null,
@ProtoNumber(3) val flag: Int = Int.MIN_VALUE,
@ProtoNumber(5) val receiver: Long? = null,
@ProtoNumber(6) val receiverUid: String? = null,
)
@Serializable
data class MessageContentHead(
@ProtoNumber(1) val msgType: Int = Int.MIN_VALUE,
@ProtoNumber(2) val msgSubType: Int = Int.MIN_VALUE,
@ProtoNumber(4) val u1: Long = Long.MIN_VALUE,
@ProtoNumber(5) val msgSeq: Long = Long.MIN_VALUE,
@ProtoNumber(6) val msgTime: Long? = null,
@ProtoNumber(7) val u2: Int? = null,
@ProtoNumber(11) val u3: Long? = null,
@ProtoNumber(12) val msgRandom: Long = Long.MIN_VALUE,
@ProtoNumber(14) val u4: Long? = null,
@ProtoNumber(28) val u5: Long? = null,
data class Font(
@ProtoNumber(9) val fontName: String? = null
)

View File

@ -0,0 +1,10 @@
package protobuf.message
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class MessageBody(
@ProtoNumber(1) val rich: RichMessage? = null,
@ProtoNumber(2) val rawBuffer: ByteArray? = null,
)

View File

@ -0,0 +1,18 @@
package protobuf.message
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class MessageContent(
@ProtoNumber(1) val msgType: Int = Int.MIN_VALUE,
@ProtoNumber(2) val msgSubType: Int = Int.MIN_VALUE,
@ProtoNumber(4) val msgViaRandom: Long = Long.MIN_VALUE,
@ProtoNumber(5) val msgSeq: Long = Long.MIN_VALUE,
@ProtoNumber(6) val msgTime: Long? = null,
@ProtoNumber(7) val u2: Int? = null,
@ProtoNumber(11) val u3: Long? = null,
@ProtoNumber(12) val msgRandom: Long = Long.MIN_VALUE,
@ProtoNumber(14) val u4: Long? = null,
@ProtoNumber(28) val u5: Long? = null,
)

View File

@ -0,0 +1,12 @@
package protobuf.message
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import protobuf.message.element.*
@Serializable
data class MessageElement(
@ProtoNumber(1) val text: TextElement? = null,
@ProtoNumber(51) val json: JsonElement? = null,
@ProtoNumber(53) val richMedia: RichMediaElement? = null,
)

View File

@ -0,0 +1,24 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.message
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class MessageHead(
@ProtoNumber(1) val peer: Long = Long.MIN_VALUE,
@ProtoNumber(2) val peerUid: String? = null,
@ProtoNumber(3) val flag: Int = Int.MIN_VALUE,
@ProtoNumber(4) val appId: Int = Int.MIN_VALUE,
@ProtoNumber(5) val receiver: Long? = null,
@ProtoNumber(6) val receiverUid: String? = null,
@ProtoNumber(8) val groupInfo: GroupInfo? = null,
)
@Serializable
data class GroupInfo(
@ProtoNumber(1) val groupCode: ULong = ULong.MIN_VALUE,
@ProtoNumber(4) val memberCard: String? = null,
@ProtoNumber(7) val groupName: String? = null,
)

View File

@ -0,0 +1,11 @@
package protobuf.message
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class NtMessage(
@ProtoNumber(1) val msgHead: MessageHead? = null,
@ProtoNumber(2) val contentHead: MessageContent? = null,
@ProtoNumber(3) val body: MessageBody? = null,
)

View File

@ -0,0 +1,9 @@
package protobuf.message.element
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class JsonElement(
@ProtoNumber(1) val data: ByteArray? = null,
)

View File

@ -0,0 +1,13 @@
package protobuf.message.element
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class RichMediaElement(
@ProtoNumber(1) val type: Int? = null,
@ProtoNumber(2) val data: ByteArray? = null,
@ProtoNumber(3) val u1: Int? = null,
)

View File

@ -0,0 +1,9 @@
package protobuf.message.element
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class TextElement(
@ProtoNumber(1) val text: String? = null,
)

View File

@ -0,0 +1,54 @@
@file:OptIn(ExperimentalSerializationApi::class)
package protobuf.message.multimedia
import kotlinx.serialization.ExperimentalSerializationApi
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
@Serializable
data class RichMediaForPicData(
@ProtoNumber(1) val info: MediaInfo?,
@ProtoNumber(2) val display: DisplayMediaInfo?,
) {
companion object {
@Serializable
data class MediaInfo(
@ProtoNumber(1) val picture: Picture? = null,
)
@Serializable
data class Picture(
@ProtoNumber(1) val info: PictureInfo? = null,
@ProtoNumber(2) val fileId: String? = null,
@ProtoNumber(4) val time: ULong? = null,
)
@Serializable
data class PictureInfo(
@ProtoNumber(2) val md5Hex: String? = null,
@ProtoNumber(3) val sha: String? = null,
@ProtoNumber(4) val name: String? = null,
@ProtoNumber(6) val width: Int? = null,
@ProtoNumber(7) val height: Int? = null,
)
}
}
@Serializable
data class DisplayMediaInfo(
@ProtoNumber(1) val show: Show? = null,
) {
companion object {
@Serializable
data class Show(
@ProtoNumber(2) val text: String? = null,
@ProtoNumber(12) val download: Download? = null
)
@Serializable
data class Download(
@ProtoNumber(30) val url: String? = null,
)
}
}

View File

@ -2,11 +2,11 @@ package protobuf.push
import kotlinx.serialization.Serializable
import kotlinx.serialization.protobuf.ProtoNumber
import protobuf.message.MessageBody
import protobuf.message.NtMessage
@Serializable
data class MessagePush(
@ProtoNumber(1) val msgBody: MessageBody? = null,
@ProtoNumber(1) val msgBody: NtMessage? = null,
@ProtoNumber(4) val clientInfo: MessagePushClientInfo? = null,
)

View File

@ -15,13 +15,13 @@ import moe.fuqiuluo.shamrock.remote.action.handlers.GetHistoryMsg
import moe.fuqiuluo.shamrock.remote.service.listener.AioListener
import moe.fuqiuluo.shamrock.tools.broadcast
import moe.fuqiuluo.shamrock.utils.DeflateTools
import protobuf.message.JsonElement
import protobuf.message.MessageBody
import protobuf.message.MessageContentHead
import protobuf.message.element.JsonElement
import protobuf.message.NtMessage
import protobuf.message.MessageContent
import protobuf.message.MessageElement
import protobuf.message.MessageElementList
import protobuf.message.MessageHead
import protobuf.message.RichMessage
import protobuf.message.MessageHead
import protobuf.message.MessageBody
import protobuf.push.MessagePush
import mqq.app.MobileQQ
import kotlin.coroutines.resume
@ -51,7 +51,7 @@ internal object PacketSvc: BaseSvc() {
val msgSeq = (latestMsg?.msgSeq ?: 0) + 1
val msgPush = MessagePush(
msgBody = MessageBody(
msgBody = NtMessage(
msgHead = MessageHead(
peer = app.longAccountUin,
peerUid = app.currentUid,
@ -59,7 +59,7 @@ internal object PacketSvc: BaseSvc() {
receiver = app.longAccountUin,
receiverUid = app.currentUid
),
contentHead = MessageContentHead(
contentHead = MessageContent(
msgType = 166,
msgSubType = 11,
msgSeq = msgSeq,
@ -71,7 +71,7 @@ internal object PacketSvc: BaseSvc() {
u4 = msgSeq - 2,
u5 = msgSeq
),
richMsg = RichMessage(MessageElementList(builder()))
body = MessageBody(RichMessage(builder()))
)
)

View File

@ -40,6 +40,7 @@ private const val GPRO_PIC_NT = "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()
@ -169,7 +170,7 @@ internal object RichProtoSvc: BaseSvc() {
val domain = if (originalUrl.startsWith("/download")) GPRO_PIC_NT else GPRO_PIC
if (originalUrl.isNotEmpty()) {
if (!originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl&rkey=CAQSKAB6JWENi5LMk0kc62l8Pm3Jn1dsLZHyRLAnNmHGoZ3y_gDZPqZt-64"
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
}
return "https://$domain$originalUrl"
}
@ -190,10 +191,11 @@ internal object RichProtoSvc: BaseSvc() {
originalUrl: String,
md5: String
): String {
val domain = if (originalUrl.startsWith("/download") ||
originalUrl.contains("rkey=")) GPRO_PIC_NT
else GPRO_PIC
val domain = if (originalUrl.startsWith("/download")) GPRO_PIC_NT else GPRO_PIC
if (originalUrl.isNotEmpty()) {
if (!originalUrl.contains("rkey=")) {
return "https://$domain$originalUrl&rkey=$multiMediaRKey"
}
return "https://$domain$originalUrl"
}
return "https://$domain/qmeetpic/0/0-0-${md5.uppercase()}/0?term=2"

View File

@ -2,7 +2,7 @@ package moe.fuqiuluo.shamrock.helper
internal abstract class InternalMessageMakerError(why: String): RuntimeException(why)
internal class ParamsException(key: String): InternalMessageMakerError("Lack of param $key")
internal class ParamsException(key: String): InternalMessageMakerError("Lack of param `$key`")
internal class IllegalParamsException(key: String): InternalMessageMakerError("Illegal param $key")

View File

@ -233,7 +233,7 @@ internal object MessageHelper {
return if (!message.isEmpty()) {
val service = QRoute.api(IMsgService::class.java)
return suspendCancellableCoroutine {
service.sendMsg(contact, uniseq.qqMsgId, msg) { code, why ->
service.sendMsg(contact, uniseq.qqMsgId, msg) { _, _ ->
it.resume(uniseq.copy(msgTime = System.currentTimeMillis()))
}
}

View File

@ -1,5 +1,4 @@
@file:OptIn(DelicateCoroutinesApi::class, ExperimentalSerializationApi::class)
package moe.fuqiuluo.shamrock.remote.service.listener
import com.tencent.qqnt.kernel.nativeinterface.MsgConstant
@ -19,6 +18,7 @@ import moe.fuqiuluo.qqinterface.servlet.FriendSvc.requestFriendSystemMsgNew
import moe.fuqiuluo.qqinterface.servlet.GroupSvc
import moe.fuqiuluo.qqinterface.servlet.GroupSvc.requestGroupSystemMsgNew
import moe.fuqiuluo.qqinterface.servlet.TicketSvc.getLongUin
import moe.fuqiuluo.qqinterface.servlet.transfile.RichProtoSvc
import moe.fuqiuluo.shamrock.helper.MessageHelper
import moe.fuqiuluo.shamrock.remote.service.data.push.NoticeSubType
import moe.fuqiuluo.shamrock.remote.service.data.push.NoticeType
@ -31,9 +31,10 @@ 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 protobuf.message.MessageContentHead
import protobuf.message.MessageContent
import protobuf.message.MessageHead
import protobuf.message.RichMessage
import protobuf.message.MessageBody
import protobuf.message.multimedia.RichMediaForPicData
import protobuf.push.C2CCommonTipsEvent
import protobuf.push.C2CRecallEvent
import protobuf.push.FriendApplyEvent
@ -46,6 +47,9 @@ import protobuf.push.GroupInvitedApplyEvent
import protobuf.push.GroupListChangeEvent
import protobuf.push.MessagePush
import protobuf.push.MessagePushClientInfo
import java.util.regex.Pattern
private val RKEY_PATTERN = Pattern.compile("rkey=([A-Za-z0-9_-]+)")
internal object PrimitiveListener {
fun registerListener() {
@ -65,7 +69,7 @@ internal object PrimitiveListener {
if (
push.msgBody == null ||
push.msgBody!!.contentHead == null ||
push.msgBody!!.richMsg == null ||
push.msgBody!!.body == null ||
push.msgBody!!.contentHead!!.msgTime == null
) return
val msgBody = push.msgBody!!
@ -73,29 +77,30 @@ internal object PrimitiveListener {
val msgType = contentHead.msgType
val subType = contentHead.msgSubType
val msgTime = contentHead.msgTime!!
val richMsg = msgBody.richMsg!!
val body = msgBody.body!!
try {
when (msgType) {
33 -> onGroupMemIncreased(msgTime, richMsg)
34 -> onGroupMemberDecreased(msgTime, richMsg)
44 -> onGroupAdminChange(msgTime, richMsg)
84 -> onGroupApply(msgTime, contentHead, richMsg)
87 -> onInviteGroup(msgTime, msgBody.msgHead!!, richMsg)
33 -> onGroupMemIncreased(msgTime, body)
34 -> onGroupMemberDecreased(msgTime, body)
44 -> onGroupAdminChange(msgTime, body)
82 -> onGroupMessage(msgTime, body)
84 -> onGroupApply(msgTime, contentHead, body)
87 -> onInviteGroup(msgTime, msgBody.msgHead!!, body)
528 -> when (subType) {
35 -> onFriendApply(msgTime, push.clientInfo!!, richMsg)
39 -> onCardChange(msgTime, richMsg)
35 -> onFriendApply(msgTime, push.clientInfo!!, body)
39 -> onCardChange(msgTime, body)
// invite
68 -> onGroupApply(msgTime, contentHead, richMsg)
138 -> onC2CRecall(msgTime, richMsg)
290 -> onC2CPoke(msgTime, richMsg)
68 -> onGroupApply(msgTime, contentHead, body)
138 -> onC2CRecall(msgTime, body)
290 -> onC2CPoke(msgTime, body)
}
732 -> when (subType) {
12 -> onGroupBan(msgTime, richMsg)
16 -> onGroupUniqueTitleChange(msgTime, richMsg)
17 -> onGroupRecall(msgTime, richMsg)
20 -> onGroupPokeAndGroupSign(msgTime, richMsg)
21 -> onEssenceMessage(msgTime, push.clientInfo, richMsg)
12 -> onGroupBan(msgTime, body)
16 -> onGroupUniqueTitleChange(msgTime, body)
17 -> onGroupRecall(msgTime, body)
20 -> onGroupPokeAndGroupSign(msgTime, body)
21 -> onEssenceMessage(msgTime, push.clientInfo, body)
}
}
} catch (e: Exception) {
@ -103,7 +108,24 @@ internal object PrimitiveListener {
}
}
private suspend fun onC2CPoke(msgTime: Long, richMsg: RichMessage) {
private fun onGroupMessage(msgTime: Long, body: MessageBody) {
body.rich?.elements?.filter {
it.richMedia != null
}?.map {
ProtoBuf.decodeFromByteArray<RichMediaForPicData>(it.richMedia!!.data!!)
}?.forEach {
it.display?.show?.download?.url?.let {
RKEY_PATTERN.matcher(it).takeIf {
it.find()
}?.group(1)?.let { rkey ->
LogCenter.log("更新NT RKEY成功$rkey")
RichProtoSvc.multiMediaRKey = rkey
}
}
}
}
private suspend fun onC2CPoke(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<C2CCommonTipsEvent>(richMsg.rawBuffer!!)
if (event.params == null) return
@ -129,7 +151,7 @@ internal object PrimitiveListener {
private suspend fun onFriendApply(
msgTime: Long,
clientInfo: MessagePushClientInfo,
richMsg: RichMessage
richMsg: MessageBody
) {
val event = ProtoBuf.decodeFromByteArray<FriendApplyEvent>(richMsg.rawBuffer!!)
if (event.head == null) return
@ -162,7 +184,7 @@ internal object PrimitiveListener {
}
private suspend fun onCardChange(msgTime: Long, richMsg: RichMessage) {
private suspend fun onCardChange(msgTime: Long, richMsg: MessageBody) {
LogCenter.log("群名片事件异常请尝试提交issue", Level.WARN)
/*try {
val readPacket = ByteReadPacket(richMsg.rawBuffer!!)
@ -206,7 +228,7 @@ internal object PrimitiveListener {
}*/
}
private suspend fun onGroupUniqueTitleChange(msgTime: Long, richMsg: RichMessage) {
private suspend fun onGroupUniqueTitleChange(msgTime: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
}.getOrElse {
@ -250,7 +272,7 @@ internal object PrimitiveListener {
private suspend fun onEssenceMessage(
msgTime: Long,
clientInfo: MessagePushClientInfo?,
richMsg: RichMessage
richMsg: MessageBody
) {
if (clientInfo == null) return
val event = runCatching {
@ -299,7 +321,7 @@ internal object PrimitiveListener {
}
private suspend fun onGroupPokeAndGroupSign(time: Long, richMsg: RichMessage) {
private suspend fun onGroupPokeAndGroupSign(time: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
}.getOrElse {
@ -353,7 +375,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onC2CRecall(time: Long, richMsg: RichMessage) {
private suspend fun onC2CRecall(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<C2CRecallEvent>(richMsg.rawBuffer!!)
val head = event.head!!
@ -378,7 +400,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupMemIncreased(time: Long, richMsg: RichMessage) {
private suspend fun onGroupMemIncreased(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupListChangeEvent>(richMsg.rawBuffer!!)
val groupCode = event.groupCode
val targetUid = event.memberUid
@ -408,7 +430,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupMemberDecreased(time: Long, richMsg: RichMessage) {
private suspend fun onGroupMemberDecreased(time: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupListChangeEvent>(richMsg.rawBuffer!!)
val groupCode = event.groupCode
val targetUid = event.memberUid
@ -441,7 +463,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupAdminChange(msgTime: Long, richMsg: RichMessage) {
private suspend fun onGroupAdminChange(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupAdminChangeEvent>(richMsg.rawBuffer!!)
val groupCode = event.groupCode
if (event.operation == null) return
@ -468,7 +490,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupBan(msgTime: Long, richMsg: RichMessage) {
private suspend fun onGroupBan(msgTime: Long, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupBanEvent>(richMsg.rawBuffer!!)
val groupCode = event.groupCode.toLong()
val operatorUid = event.operatorUid
@ -493,7 +515,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupRecall(time: Long, richMsg: RichMessage) {
private suspend fun onGroupRecall(time: Long, richMsg: MessageBody) {
val event = runCatching {
ProtoBuf.decodeFromByteArray<GroupCommonTipsEvent>(richMsg.rawBuffer!!)
}.getOrElse {
@ -527,7 +549,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onGroupApply(time: Long, contentHead: MessageContentHead, richMsg: RichMessage) {
private suspend fun onGroupApply(time: Long, contentHead: MessageContent, richMsg: MessageBody) {
when (contentHead.msgType) {
84 -> {
val event = ProtoBuf.decodeFromByteArray<GroupApplyEvent>(richMsg.rawBuffer!!)
@ -598,7 +620,7 @@ internal object PrimitiveListener {
}
}
private suspend fun onInviteGroup(time: Long, msgHead: MessageHead, richMsg: RichMessage) {
private suspend fun onInviteGroup(time: Long, msgHead: MessageHead, richMsg: MessageBody) {
val event = ProtoBuf.decodeFromByteArray<GroupInviteEvent>(richMsg.rawBuffer!!)
val groupCode = event.groupCode
val invitorUid = event.inviterUid