mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 01:30:17 +08:00
Support poke message, close #132
This commit is contained in:
parent
f7040c18fb
commit
52f8543597
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import kotlinx.io.core.buildPacket
|
||||
import kotlinx.io.core.readBytes
|
||||
import net.mamoe.mirai.message.data.At
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
|
||||
|
||||
internal fun At.toJceData(): ImMsgBody.Text {
|
||||
val text = this.toString()
|
||||
return ImMsgBody.Text(
|
||||
str = text,
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort(text.length.toShort()) // textLen
|
||||
writeByte(0) // flag, may=1
|
||||
writeInt(target.toInt()) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
internal val atAllData = ImMsgBody.Elem(
|
||||
text = ImMsgBody.Text(
|
||||
str = "@全体成员",
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort("@全体成员".length.toShort()) // textLen
|
||||
writeByte(1) // flag, may=1
|
||||
writeInt(0) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
)
|
@ -11,108 +11,25 @@
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import kotlinx.io.core.*
|
||||
import kotlinx.io.core.discardExact
|
||||
import kotlinx.io.core.readUInt
|
||||
import kotlinx.io.core.toByteArray
|
||||
import net.mamoe.mirai.LowLevelAPI
|
||||
import net.mamoe.mirai.contact.Member
|
||||
import net.mamoe.mirai.message.data.*
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.loadAs
|
||||
import net.mamoe.mirai.qqandroid.io.serialization.toByteArray
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.HummerCommelem
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgComm
|
||||
import net.mamoe.mirai.utils.*
|
||||
import net.mamoe.mirai.utils.io.encodeToString
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.read
|
||||
import net.mamoe.mirai.utils.io.toByteArray
|
||||
|
||||
internal fun At.toJceData(): ImMsgBody.Text {
|
||||
val text = this.toString()
|
||||
return ImMsgBody.Text(
|
||||
str = text,
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort(text.length.toShort()) // textLen
|
||||
writeByte(0) // flag, may=1
|
||||
writeInt(target.toInt()) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
}
|
||||
|
||||
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
return ImMsgBody.NotOnlineImage(
|
||||
filePath = this.filepath,
|
||||
resId = this.resourceId,
|
||||
oldPicMd5 = false,
|
||||
picMd5 = this.md5,
|
||||
fileLen = this.fileLength,
|
||||
picHeight = this.height,
|
||||
picWidth = this.width,
|
||||
bizType = this.bizType,
|
||||
imgType = this.imageType,
|
||||
downloadPath = this.downloadPath,
|
||||
original = this.original,
|
||||
fileId = this.fileId,
|
||||
pbReserve = byteArrayOf(0x78, 0x02)
|
||||
)
|
||||
}
|
||||
|
||||
internal val FACE_BUF = "00 01 00 04 52 CC F5 D0".hexToBytes()
|
||||
|
||||
internal fun Face.toJceData(): ImMsgBody.Face {
|
||||
return ImMsgBody.Face(
|
||||
index = this.id,
|
||||
old = (0x1445 - 4 + this.id).toShort().toByteArray(),
|
||||
buf = FACE_BUF
|
||||
)
|
||||
}
|
||||
|
||||
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
|
||||
return ImMsgBody.CustomFace(
|
||||
filePath = this.filepath,
|
||||
fileId = this.fileId,
|
||||
serverIp = this.serverIp,
|
||||
serverPort = this.serverPort,
|
||||
fileType = this.fileType,
|
||||
signature = this.signature,
|
||||
useful = this.useful,
|
||||
md5 = this.md5,
|
||||
bizType = this.bizType,
|
||||
imageType = this.imageType,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
source = this.source,
|
||||
size = this.size,
|
||||
origin = this.original,
|
||||
pbReserve = this.pbReserve,
|
||||
flag = ByteArray(4),
|
||||
//_400Height = 235,
|
||||
//_400Url = "/gchatpic_new/1040400290/1041235568-2195821338-01E9451B70EDEAE3B37C101F1EEBF5B5/400?term=2",
|
||||
//_400Width = 351,
|
||||
oldData = oldData
|
||||
)
|
||||
}
|
||||
|
||||
private val oldData: ByteArray =
|
||||
"15 36 20 39 32 6B 41 31 00 38 37 32 66 30 36 36 30 33 61 65 31 30 33 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 30 31 45 39 34 35 31 42 2D 37 30 45 44 2D 45 41 45 33 2D 42 33 37 43 2D 31 30 31 46 31 45 45 42 46 35 42 35 7D 2E 70 6E 67 41".hexToBytes()
|
||||
|
||||
|
||||
private val atAllData = ImMsgBody.Elem(
|
||||
text = ImMsgBody.Text(
|
||||
str = "@全体成员",
|
||||
attr6Buf = buildPacket {
|
||||
// MessageForText$AtTroopMemberInfo
|
||||
writeShort(1) // const
|
||||
writeShort(0) // startPos
|
||||
writeShort("@全体成员".length.toShort()) // textLen
|
||||
writeByte(1) // flag, may=1
|
||||
writeInt(0) // uin
|
||||
writeShort(0) // const
|
||||
}.readBytes()
|
||||
)
|
||||
)
|
||||
|
||||
private val UNSUPPORTED_MERGED_MESSAGE_PLAIN = PlainText("你的QQ暂不支持查看[转发多条消息],请期待后续版本。")
|
||||
private val UNSUPPORTED_POKE_MESSAGE_PLAIN = PlainText("[戳一戳]请使用最新版手机QQ体验新功能。")
|
||||
|
||||
@OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
|
||||
internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: Boolean): MutableList<ImMsgBody.Elem> {
|
||||
@ -175,6 +92,21 @@ internal fun MessageChain.toRichTextElems(forGroup: Boolean, withGeneralFlags: B
|
||||
elements.add(ImMsgBody.Elem(text = it.toJceData()))
|
||||
elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = " ")))
|
||||
}
|
||||
is PokeMessage -> {
|
||||
elements.add(
|
||||
ImMsgBody.Elem(
|
||||
commonElem = ImMsgBody.CommonElem(
|
||||
serviceType = 2,
|
||||
businessType = it.type,
|
||||
pbElem = HummerCommelem.MsgElemInfoServtype2(
|
||||
pokeType = it.type,
|
||||
vaspokeId = it.id
|
||||
).toByteArray(HummerCommelem.MsgElemInfoServtype2.serializer())
|
||||
)
|
||||
)
|
||||
)
|
||||
transformOneMessage(UNSUPPORTED_POKE_MESSAGE_PLAIN)
|
||||
}
|
||||
is OfflineGroupImage -> elements.add(ImMsgBody.Elem(customFace = it.toJceData()))
|
||||
is OnlineGroupImageImpl -> elements.add(ImMsgBody.Elem(customFace = it.delegate))
|
||||
is OnlineFriendImageImpl -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate))
|
||||
@ -231,65 +163,6 @@ private val PB_RESERVE_FOR_RICH_MESSAGE =
|
||||
"08 09 78 00 C8 01 00 F0 01 00 F8 01 00 90 02 00 C8 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 02 08 03 90 04 80 80 80 10 B8 04 00 C0 04 00".hexToBytes()
|
||||
private val PB_RESERVE_FOR_ELSE = "78 00 F8 01 00 C8 02 00".hexToBytes()
|
||||
|
||||
internal class OnlineGroupImageImpl(
|
||||
internal val delegate: ImMsgBody.CustomFace
|
||||
) : OnlineGroupImage() {
|
||||
override val filepath: String = delegate.filePath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val serverIp: Int get() = delegate.serverIp
|
||||
override val serverPort: Int get() = delegate.serverPort
|
||||
override val fileType: Int get() = delegate.fileType
|
||||
override val signature: ByteArray get() = delegate.signature
|
||||
override val useful: Int get() = delegate.useful
|
||||
override val md5: ByteArray get() = delegate.md5
|
||||
override val bizType: Int get() = delegate.bizType
|
||||
override val imageType: Int get() = delegate.imageType
|
||||
override val width: Int get() = delegate.width
|
||||
override val height: Int get() = delegate.height
|
||||
override val source: Int get() = delegate.source
|
||||
override val size: Int get() = delegate.size
|
||||
override val original: Int get() = delegate.origin
|
||||
override val pbReserve: ByteArray get() = delegate.pbReserve
|
||||
override val imageId: String = ExternalImage.generateImageId(delegate.md5, imageType)
|
||||
override val originUrl: String
|
||||
get() = "http://gchat.qpic.cn" + delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OnlineGroupImageImpl && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
internal class OnlineFriendImageImpl(
|
||||
internal val delegate: ImMsgBody.NotOnlineImage
|
||||
) : OnlineFriendImage() {
|
||||
override val resourceId: String get() = delegate.resId
|
||||
override val md5: ByteArray get() = delegate.picMd5
|
||||
override val filepath: String get() = delegate.filePath
|
||||
override val fileLength: Int get() = delegate.fileLen
|
||||
override val height: Int get() = delegate.picHeight
|
||||
override val width: Int get() = delegate.picWidth
|
||||
override val bizType: Int get() = delegate.bizType
|
||||
override val imageType: Int get() = delegate.imgType
|
||||
override val downloadPath: String get() = delegate.downloadPath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val original: Int get() = delegate.original
|
||||
override val originUrl: String
|
||||
get() = "http://c2cpicdw.qpic.cn" + this.delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OnlineFriendImageImpl && other.resourceId == this.resourceId && other.md5
|
||||
.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class)
|
||||
internal fun MsgComm.Msg.toMessageChain(): MessageChain {
|
||||
val elements = this.msgBody.richText.elems
|
||||
@ -337,6 +210,7 @@ internal inline fun <reified R> Iterable<*>.firstIsInstance(): R {
|
||||
throw NoSuchElementException("Collection contains no element matching the predicate.")
|
||||
}
|
||||
|
||||
@OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
|
||||
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
|
||||
this.forEach {
|
||||
when {
|
||||
@ -383,6 +257,22 @@ internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilde
|
||||
}
|
||||
}
|
||||
}
|
||||
it.elemFlags2 != null
|
||||
|| it.extraInfo != null
|
||||
|| it.generalFlags != null -> {
|
||||
|
||||
}
|
||||
it.commonElem != null -> {
|
||||
when (it.commonElem.serviceType) {
|
||||
2 -> {
|
||||
val proto = it.commonElem.pbElem.loadAs(HummerCommelem.MsgElemInfoServtype2.serializer())
|
||||
message.add(PokeMessage(proto.pokeType, proto.vaspokeId))
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
println(it._miraiContentToString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,16 @@
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import net.mamoe.mirai.message.data.Face
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
import net.mamoe.mirai.utils.io.toByteArray
|
||||
|
||||
internal val FACE_BUF = "00 01 00 04 52 CC F5 D0".hexToBytes()
|
||||
|
||||
internal fun Face.toJceData(): ImMsgBody.Face {
|
||||
return ImMsgBody.Face(
|
||||
index = this.id,
|
||||
old = (0x1445 - 4 + this.id).toShort().toByteArray(),
|
||||
buf = FACE_BUF
|
||||
)
|
||||
}
|
@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.message
|
||||
|
||||
import net.mamoe.mirai.message.data.OfflineFriendImage
|
||||
import net.mamoe.mirai.message.data.OfflineGroupImage
|
||||
import net.mamoe.mirai.message.data.OnlineFriendImage
|
||||
import net.mamoe.mirai.message.data.OnlineGroupImage
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody
|
||||
import net.mamoe.mirai.utils.ExternalImage
|
||||
import net.mamoe.mirai.utils.io.hexToBytes
|
||||
|
||||
|
||||
internal class OnlineGroupImageImpl(
|
||||
internal val delegate: ImMsgBody.CustomFace
|
||||
) : OnlineGroupImage() {
|
||||
override val filepath: String = delegate.filePath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val serverIp: Int get() = delegate.serverIp
|
||||
override val serverPort: Int get() = delegate.serverPort
|
||||
override val fileType: Int get() = delegate.fileType
|
||||
override val signature: ByteArray get() = delegate.signature
|
||||
override val useful: Int get() = delegate.useful
|
||||
override val md5: ByteArray get() = delegate.md5
|
||||
override val bizType: Int get() = delegate.bizType
|
||||
override val imageType: Int get() = delegate.imageType
|
||||
override val width: Int get() = delegate.width
|
||||
override val height: Int get() = delegate.height
|
||||
override val source: Int get() = delegate.source
|
||||
override val size: Int get() = delegate.size
|
||||
override val original: Int get() = delegate.origin
|
||||
override val pbReserve: ByteArray get() = delegate.pbReserve
|
||||
override val imageId: String = ExternalImage.generateImageId(delegate.md5, imageType)
|
||||
override val originUrl: String
|
||||
get() = "http://gchat.qpic.cn" + delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OnlineGroupImageImpl && other.filepath == this.filepath && other.md5.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
internal class OnlineFriendImageImpl(
|
||||
internal val delegate: ImMsgBody.NotOnlineImage
|
||||
) : OnlineFriendImage() {
|
||||
override val resourceId: String get() = delegate.resId
|
||||
override val md5: ByteArray get() = delegate.picMd5
|
||||
override val filepath: String get() = delegate.filePath
|
||||
override val fileLength: Int get() = delegate.fileLen
|
||||
override val height: Int get() = delegate.picHeight
|
||||
override val width: Int get() = delegate.picWidth
|
||||
override val bizType: Int get() = delegate.bizType
|
||||
override val imageType: Int get() = delegate.imgType
|
||||
override val downloadPath: String get() = delegate.downloadPath
|
||||
override val fileId: Int get() = delegate.fileId
|
||||
override val original: Int get() = delegate.original
|
||||
override val originUrl: String
|
||||
get() = "http://c2cpicdw.qpic.cn" + this.delegate.origUrl
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is OnlineFriendImageImpl && other.resourceId == this.resourceId && other.md5
|
||||
.contentEquals(this.md5)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return imageId.hashCode() + 31 * md5.hashCode()
|
||||
}
|
||||
}
|
||||
|
||||
internal fun OfflineGroupImage.toJceData(): ImMsgBody.CustomFace {
|
||||
return ImMsgBody.CustomFace(
|
||||
filePath = this.filepath,
|
||||
fileId = this.fileId,
|
||||
serverIp = this.serverIp,
|
||||
serverPort = this.serverPort,
|
||||
fileType = this.fileType,
|
||||
signature = this.signature,
|
||||
useful = this.useful,
|
||||
md5 = this.md5,
|
||||
bizType = this.bizType,
|
||||
imageType = this.imageType,
|
||||
width = this.width,
|
||||
height = this.height,
|
||||
source = this.source,
|
||||
size = this.size,
|
||||
origin = this.original,
|
||||
pbReserve = this.pbReserve,
|
||||
flag = ByteArray(4),
|
||||
//_400Height = 235,
|
||||
//_400Url = "/gchatpic_new/1040400290/1041235568-2195821338-01E9451B70EDEAE3B37C101F1EEBF5B5/400?term=2",
|
||||
//_400Width = 351,
|
||||
oldData = oldData
|
||||
)
|
||||
}
|
||||
|
||||
private val oldData: ByteArray =
|
||||
"15 36 20 39 32 6B 41 31 00 38 37 32 66 30 36 36 30 33 61 65 31 30 33 62 37 20 20 20 20 20 20 35 30 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 20 7B 30 31 45 39 34 35 31 42 2D 37 30 45 44 2D 45 41 45 33 2D 42 33 37 43 2D 31 30 31 46 31 45 45 42 46 35 42 35 7D 2E 70 6E 67 41".hexToBytes()
|
||||
|
||||
|
||||
internal fun OfflineFriendImage.toJceData(): ImMsgBody.NotOnlineImage {
|
||||
return ImMsgBody.NotOnlineImage(
|
||||
filePath = this.filepath,
|
||||
resId = this.resourceId,
|
||||
oldPicMd5 = false,
|
||||
picMd5 = this.md5,
|
||||
fileLen = this.fileLength,
|
||||
picHeight = this.height,
|
||||
picWidth = this.width,
|
||||
bizType = this.bizType,
|
||||
imgType = this.imageType,
|
||||
downloadPath = this.downloadPath,
|
||||
original = this.original,
|
||||
fileId = this.fileId,
|
||||
pbReserve = byteArrayOf(0x78, 0x02)
|
||||
)
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
package net.mamoe.mirai.qqandroid.network.protocol.data.proto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.protobuf.ProtoId
|
||||
import net.mamoe.mirai.qqandroid.io.ProtoBuf
|
||||
import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
|
||||
|
||||
internal class HummerCommelem : ProtoBuf {
|
||||
@Serializable
|
||||
class MsgElemInfoServtype1(
|
||||
@ProtoId(1) val rewardId: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val senderUin: Long = 0L,
|
||||
@ProtoId(3) val picType: Int = 0,
|
||||
@ProtoId(4) val rewardMoney: Int = 0,
|
||||
@ProtoId(5) val url: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val content: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val createTimestamp: Int = 0,
|
||||
@ProtoId(8) val status: Int = 0,
|
||||
@ProtoId(9) val size: Int = 0,
|
||||
@ProtoId(10) val videoDuration: Int = 0,
|
||||
@ProtoId(11) val seq: Long = 0L,
|
||||
@ProtoId(12) val rewardTypeExt: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype11(
|
||||
@ProtoId(1) val resID: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val resMD5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val reserveInfo1: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val reserveInfo2: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val doodleDataOffset: Int = 0,
|
||||
@ProtoId(6) val doodleGifId: Int = 0,
|
||||
@ProtoId(7) val doodleUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val doodleMd5: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype13(
|
||||
@ProtoId(1) val sysHeadId: Int = 0,
|
||||
@ProtoId(2) val headFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype14(
|
||||
@ProtoId(1) val id: Int = 0,
|
||||
@ProtoId(2) val reserveInfo: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype15(
|
||||
@ProtoId(1) val vid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val cover: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val title: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val summary: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val createTime: Long = 0L,
|
||||
@ProtoId(6) val commentContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val author: Long = 0L,
|
||||
@ProtoId(8) val ctrVersion: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype16(
|
||||
@ProtoId(1) val uid: Long = 0L,
|
||||
@ProtoId(2) val unionID: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val storyID: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val md5: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val thumbUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val doodleUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val videoWidth: Int = 0,
|
||||
@ProtoId(8) val videoHeight: Int = 0,
|
||||
@ProtoId(9) val sourceName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(10) val sourceActionType: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(11) val sourceActionData: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(12) val ctrVersion: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype18(
|
||||
@ProtoId(1) val currentAmount: Long = 0L,
|
||||
@ProtoId(2) val totalAmount: Long = 0L,
|
||||
@ProtoId(3) val listid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val authKey: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val number: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype19(
|
||||
@ProtoId(1) val data: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype2(
|
||||
@ProtoId(1) val pokeType: Int = 0,
|
||||
@ProtoId(2) val pokeSummary: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val doubleHit: Int = 0,
|
||||
@ProtoId(4) val vaspokeId: Int = 0,
|
||||
@ProtoId(5) val vaspokeName: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val vaspokeMinver: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val pokeStrength: Int = 0,
|
||||
@ProtoId(8) val msgType: Int = 0,
|
||||
@ProtoId(9) val faceBubbleCount: Int = 0,
|
||||
@ProtoId(10) val pokeFlag: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype20(
|
||||
@ProtoId(1) val data: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype21(
|
||||
@ProtoId(1) val topicId: Int = 0,
|
||||
@ProtoId(2) val confessorUin: Long = 0L,
|
||||
@ProtoId(3) val confessorNick: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val confessorSex: Int = 0,
|
||||
@ProtoId(5) val sysmsgFlag: Int = 0,
|
||||
@ProtoId(6) val c2cConfessCtx: HummerCommelem.MsgElemInfoServtype21.C2CConfessContext? = null,
|
||||
@ProtoId(7) val topic: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(8) val confessTime: Long = 0L,
|
||||
@ProtoId(9) val groupConfessMsg: HummerCommelem.MsgElemInfoServtype21.GroupConfessMsg? = null,
|
||||
@ProtoId(10) val groupConfessCtx: HummerCommelem.MsgElemInfoServtype21.GroupConfessContext? = null
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class C2CConfessContext(
|
||||
@ProtoId(1) val confessorUin: Long = 0L,
|
||||
@ProtoId(2) val confessToUin: Long = 0L,
|
||||
@ProtoId(3) val sendUin: Long = 0L,
|
||||
@ProtoId(4) val confessorNick: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val confess: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val bgType: Int = 0,
|
||||
@ProtoId(7) val topicId: Int = 0,
|
||||
@ProtoId(8) val confessTime: Long = 0L,
|
||||
@ProtoId(9) val confessorSex: Int = 0,
|
||||
@ProtoId(10) val bizType: Int = 0,
|
||||
@ProtoId(11) val confessNum: Int = 0,
|
||||
@ProtoId(12) val confessToSex: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupConfessContext(
|
||||
@ProtoId(1) val confessorUin: Long = 0L,
|
||||
@ProtoId(2) val confessToUin: Long = 0L,
|
||||
@ProtoId(3) val sendUin: Long = 0L,
|
||||
@ProtoId(4) val confessorSex: Int = 0,
|
||||
@ProtoId(5) val confessToNick: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(6) val topic: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val topicId: Int = 0,
|
||||
@ProtoId(8) val confessTime: Long = 0L,
|
||||
@ProtoId(9) val confessToNickType: Int = 0,
|
||||
@ProtoId(10) val confessorNick: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupConfessItem(
|
||||
@ProtoId(1) val topicId: Int = 0,
|
||||
@ProtoId(2) val confessToUin: Long = 0L,
|
||||
@ProtoId(3) val confessToNick: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val topic: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val confessToNickType: Int = 0
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class GroupConfessMsg(
|
||||
@ProtoId(1) val confessTime: Long = 0L,
|
||||
@ProtoId(2) val confessorUin: Long = 0L,
|
||||
@ProtoId(3) val confessorSex: Int = 0,
|
||||
@ProtoId(4) val sysmsgFlag: Int = 0,
|
||||
@ProtoId(5) val confessItems: List<HummerCommelem.MsgElemInfoServtype21.GroupConfessItem>? = null,
|
||||
@ProtoId(6) val totalTopicCount: Int = 0
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype23(
|
||||
@ProtoId(1) val faceType: Int = 0,
|
||||
@ProtoId(2) val faceBubbleCount: Int = 0,
|
||||
@ProtoId(3) val faceSummary: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val flag: Int = 0,
|
||||
@ProtoId(5) val others: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype24(
|
||||
@ProtoId(1) val limitChatEnter: HummerCommelem.MsgElemInfoServtype24.LimitChatEnter? = null,
|
||||
@ProtoId(2) val limitChatExit: HummerCommelem.MsgElemInfoServtype24.LimitChatExit? = null
|
||||
) : ProtoBuf {
|
||||
@Serializable
|
||||
class LimitChatEnter(
|
||||
@ProtoId(1) val tipsWording: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val leftChatTime: Int = 0,
|
||||
@ProtoId(3) val matchTs: Long = 0L,
|
||||
@ProtoId(4) val matchExpiredTime: Int = 0,
|
||||
@ProtoId(5) val c2cExpiredTime: Int = 0,
|
||||
@ProtoId(6) val readyTs: Long = 0L,
|
||||
@ProtoId(7) val matchNick: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class LimitChatExit(
|
||||
@ProtoId(1) val exitMethod: Int = 0,
|
||||
@ProtoId(2) val matchTs: Long = 0L
|
||||
) : ProtoBuf
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype27(
|
||||
@ProtoId(1) val videoFile: ImMsgBody.VideoFile? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype29(
|
||||
@ProtoId(1) val luckybagMsg: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype3(
|
||||
@ProtoId(1) val flashTroopPic: ImMsgBody.CustomFace? = null,
|
||||
@ProtoId(2) val flashC2cPic: ImMsgBody.NotOnlineImage? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype31(
|
||||
@ProtoId(1) val text: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val ext: ByteArray = EMPTY_BYTE_ARRAY
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype4(
|
||||
@ProtoId(1) val imsgType: Int = 0,
|
||||
@ProtoId(4) val stStoryAioObjMsg: HummerCommelem.StoryAioObjMsg? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype5(
|
||||
@ProtoId(1) val vid: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(2) val cover: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val title: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(4) val summary: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val createTime: Long = 0L,
|
||||
@ProtoId(6) val commentContent: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(7) val author: Long = 0L
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype8(
|
||||
@ProtoId(1) val wifiDeliverGiftMsg: ImMsgBody.DeliverGiftMsg? = null
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class MsgElemInfoServtype9(
|
||||
@ProtoId(1) val anchorStatus: Int = 0,
|
||||
@ProtoId(2) val jumpSchema: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(3) val anchorNickname: String = "",
|
||||
@ProtoId(4) val anchorHeadUrl: ByteArray = EMPTY_BYTE_ARRAY,
|
||||
@ProtoId(5) val liveTitle: String = ""
|
||||
) : ProtoBuf
|
||||
|
||||
@Serializable
|
||||
class StoryAioObjMsg(
|
||||
@ProtoId(1) val uiUrl: String = "",
|
||||
@ProtoId(2) val jmpUrl: String = ""
|
||||
) : ProtoBuf
|
||||
}
|
@ -223,9 +223,7 @@ internal class MessageSvc {
|
||||
|
||||
friend.lastMessageSequence.loop { instant ->
|
||||
if (msg.msgHead.msgSeq > instant) {
|
||||
println("bigger")
|
||||
if (friend.lastMessageSequence.compareAndSet(instant, msg.msgHead.msgSeq)) {
|
||||
println("set ok")
|
||||
return@mapNotNull FriendMessage(
|
||||
friend,
|
||||
msg.toMessageChain()
|
||||
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2020 Mamoe Technologies and contributors.
|
||||
*
|
||||
* 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
|
||||
* Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
|
||||
*
|
||||
* https://github.com/mamoe/mirai/blob/master/LICENSE
|
||||
*/
|
||||
|
||||
@file:Suppress("unused")
|
||||
|
||||
package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.jvm.JvmField
|
||||
|
||||
@SinceMirai("0.31.0")
|
||||
@MiraiExperimentalAPI
|
||||
sealed class HummerMessage : MessageContent {
|
||||
companion object Key : Message.Key<HummerMessage>
|
||||
}
|
||||
|
||||
/**
|
||||
* 戳一戳
|
||||
*/
|
||||
@MiraiExperimentalAPI
|
||||
@SinceMirai("0.31.0")
|
||||
@OptIn(MiraiInternalAPI::class)
|
||||
class PokeMessage @MiraiInternalAPI(message = "使用伴生对象中的常量") constructor(
|
||||
val type: Int,
|
||||
@MiraiExperimentalAPI
|
||||
val id: Int
|
||||
) : HummerMessage() {
|
||||
companion object Types : Message.Key<PokeMessage> {
|
||||
/**
|
||||
* 戳一戳
|
||||
*/
|
||||
@JvmField
|
||||
val Poke = PokeMessage(1, -1)
|
||||
|
||||
/**
|
||||
* 比心
|
||||
*/
|
||||
@JvmField
|
||||
val ShowLove = PokeMessage(2, -1)
|
||||
|
||||
/**
|
||||
* 点赞
|
||||
*/
|
||||
@JvmField
|
||||
val Like = PokeMessage(3, -1)
|
||||
|
||||
/**
|
||||
* 心碎
|
||||
*/
|
||||
@JvmField
|
||||
val Heartbroken = PokeMessage(4, -1)
|
||||
|
||||
/**
|
||||
* 666
|
||||
*/
|
||||
@JvmField
|
||||
val SixSixSix = PokeMessage(5, -1)
|
||||
|
||||
/**
|
||||
* 放大招
|
||||
*/
|
||||
@JvmField
|
||||
val FangDaZhao = PokeMessage(6, -1)
|
||||
}
|
||||
|
||||
private val stringValue = "[mirai:Poke($type, $id)]"
|
||||
|
||||
override fun toString(): String = stringValue
|
||||
override val length: Int get() = stringValue.length
|
||||
override fun get(index: Int): Char = stringValue[index]
|
||||
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
|
||||
stringValue.subSequence(startIndex, endIndex)
|
||||
|
||||
override fun compareTo(other: String): Int = stringValue.compareTo(other)
|
||||
|
||||
//businessType=0x00000001(1)
|
||||
//pbElem=08 01 18 00 20 FF FF FF FF 0F 2A 00 32 00 38 00 50 00
|
||||
//serviceType=0x00000002(2)
|
||||
}
|
@ -15,6 +15,7 @@ package net.mamoe.mirai.message.data
|
||||
|
||||
import net.mamoe.mirai.message.data.NullMessageChain.equals
|
||||
import net.mamoe.mirai.message.data.NullMessageChain.toString
|
||||
import net.mamoe.mirai.utils.MiraiExperimentalAPI
|
||||
import net.mamoe.mirai.utils.MiraiInternalAPI
|
||||
import net.mamoe.mirai.utils.SinceMirai
|
||||
import kotlin.js.JsName
|
||||
@ -44,6 +45,10 @@ import kotlin.reflect.KProperty
|
||||
*/
|
||||
interface MessageChain : Message, Iterable<SingleMessage> {
|
||||
override operator fun contains(sub: String): Boolean
|
||||
|
||||
/**
|
||||
* 得到易读的字符串
|
||||
*/
|
||||
override fun toString(): String
|
||||
|
||||
/**
|
||||
@ -127,25 +132,27 @@ inline fun <reified M : Message> MessageChain.any(): Boolean = this.any { it is
|
||||
/**
|
||||
* 获取第一个 [M] 类型的 [Message] 实例
|
||||
*/
|
||||
@OptIn(ExperimentalMessageSource::class)
|
||||
@OptIn(ExperimentalMessageSource::class, MiraiExperimentalAPI::class)
|
||||
@JvmSynthetic
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun <M : Message> MessageChain.firstOrNull(key: Message.Key<M>): M? = when (key) {
|
||||
At -> first<At>()
|
||||
AtAll -> first<AtAll>()
|
||||
PlainText -> first<PlainText>()
|
||||
Image -> first<Image>()
|
||||
OnlineImage -> first<OnlineImage>()
|
||||
OfflineImage -> first<OfflineImage>()
|
||||
GroupImage -> first<GroupImage>()
|
||||
FriendImage -> first<FriendImage>()
|
||||
Face -> first<Face>()
|
||||
QuoteReply -> first<QuoteReply>()
|
||||
MessageSource -> first<MessageSource>()
|
||||
XmlMessage -> first<XmlMessage>()
|
||||
JsonMessage -> first<JsonMessage>()
|
||||
RichMessage -> first<RichMessage>()
|
||||
LightApp -> first<LightApp>()
|
||||
At -> firstOrNull<At>()
|
||||
AtAll -> firstOrNull<AtAll>()
|
||||
PlainText -> firstOrNull<PlainText>()
|
||||
Image -> firstOrNull<Image>()
|
||||
OnlineImage -> firstOrNull<OnlineImage>()
|
||||
OfflineImage -> firstOrNull<OfflineImage>()
|
||||
GroupImage -> firstOrNull<GroupImage>()
|
||||
FriendImage -> firstOrNull<FriendImage>()
|
||||
Face -> firstOrNull<Face>()
|
||||
QuoteReply -> firstOrNull<QuoteReply>()
|
||||
MessageSource -> firstOrNull<MessageSource>()
|
||||
XmlMessage -> firstOrNull<XmlMessage>()
|
||||
JsonMessage -> firstOrNull<JsonMessage>()
|
||||
RichMessage -> firstOrNull<RichMessage>()
|
||||
LightApp -> firstOrNull<LightApp>()
|
||||
PokeMessage -> firstOrNull<PokeMessage>()
|
||||
HummerMessage -> firstOrNull<HummerMessage>()
|
||||
else -> null
|
||||
} as M?
|
||||
|
||||
|
@ -163,6 +163,13 @@ class LongMessage(override val content: String, val resId: String) : RichMessage
|
||||
override fun toString(): String = content
|
||||
}
|
||||
|
||||
/*
|
||||
commonElem=CommonElem#750141174 {
|
||||
businessType=0x00000001(1)
|
||||
pbElem=08 01 18 00 20 FF FF FF FF 0F 2A 00 32 00 38 00 50 00
|
||||
serviceType=0x00000002(2)
|
||||
}
|
||||
*/
|
||||
|
||||
/**
|
||||
* 构造一条 XML 消息
|
||||
|
Loading…
Reference in New Issue
Block a user