From f244a12b8ba86c46967fa721de5cc0ce4b3b70d2 Mon Sep 17 00:00:00 2001 From: Him188 Date: Tue, 4 Feb 2020 17:11:57 +0800 Subject: [PATCH] Fix image --- .../net/mamoe/mirai/qqandroid/ContactImpl.kt | 19 ++++-- .../network/protocol/data/jce/TroopList.kt | 4 +- .../chat/receive/OnlinePush.PbPushGroupMsg.kt | 1 + .../mamoe/mirai/qqandroid/utils/MessageQQA.kt | 66 +++++++++++-------- .../net.mamoe.mirai/message/MessagePacket.kt | 4 ++ .../kotlin/net.mamoe.mirai/message/data/At.kt | 12 ++-- .../net.mamoe.mirai/message/data/Image.kt | 6 ++ .../net.mamoe.mirai/utils/ExternalImage.kt | 4 +- mirai-demos/mirai-demo-gentleman/build.gradle | 1 + .../src/main/kotlin/demo/gentleman/Main.kt | 3 +- .../mirai/imageplugin/ImageSenderMain.kt | 3 +- 11 files changed, 79 insertions(+), 44 deletions(-) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt index 04b491851..61e616291 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt @@ -4,6 +4,7 @@ import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.FriendNameRemark import net.mamoe.mirai.data.PreviousNameList import net.mamoe.mirai.data.Profile +import net.mamoe.mirai.message.data.CustomFaceFromFile import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.message.data.NotOnlineImageFromFile @@ -206,14 +207,24 @@ internal class GroupImpl( } socket.close() val resourceId = image.calculateImageResourceId() - return NotOnlineImageFromFile( - resourceId = resourceId, + + return CustomFaceFromFile( md5 = image.md5, filepath = resourceId, - fileLength = image.inputSize.toInt(), + fileId = response.fileId.toInt(), + fileType = 66, // ? height = image.height, width = image.width, - imageType = image.imageType + imageType = image.imageType, + bizType = 0, + serverIp = response.uploadIpList.first(), + serverPort = response.uploadPortList.first(), + signature = image.md5, + size = image.inputSize.toInt(), + useful = 1, + source = 200, + origin = 1, + pbReserve = byteArrayOf(0x78, 0x02) ) } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt index c195bdbb1..7ad3c3764 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/jce/TroopList.kt @@ -140,8 +140,8 @@ internal class stTroopMemberInfo( @SerialId(3) val gender: Byte, @SerialId(4) val nick: String = "", @SerialId(5) val status: Byte = 20, - @SerialId(6) val sShowName: String? = "", - @SerialId(8) val sName: String? = "", + @SerialId(6) val sShowName: String? = null, + @SerialId(8) val sName: String? = null, @SerialId(9) val cGender: Byte? = null, @SerialId(10) val sPhone: String? = "", @SerialId(11) val sEmail: String? = "", diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt index 778d217c3..7a87eb046 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt @@ -40,6 +40,7 @@ internal class OnlinePush { val group = bot.getGroup(pbPushMsg.msg.msgHead.groupInfo!!.groupCode) + // println(pbPushMsg.msg.msgBody.richText.contentToString()) val flags = extraInfo?.flags ?: 0 return GroupMessageOrNull( GroupMessage( diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt index 321622aaa..ebb39aaa8 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/utils/MessageQQA.kt @@ -1,9 +1,23 @@ package net.mamoe.mirai.qqandroid.utils -import net.mamoe.mirai.data.ImageLink +import kotlinx.io.core.readUInt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody +import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.io.discardExact import net.mamoe.mirai.utils.io.hexToBytes +import net.mamoe.mirai.utils.io.read +import net.mamoe.mirai.utils.io.toByteArray + +private val AT_BUF_1 = byteArrayOf(0x00, 0x01, 0x00, 0x00, 0x00, 0x0A, 0x00) +private val AT_BUF_2 = ByteArray(2) + +internal fun At.toJceData(): ImMsgBody.Text { + return ImMsgBody.Text( + str = this.toString(), + attr6Buf = AT_BUF_1 + this.target.toInt().toByteArray() + AT_BUF_2 + ) +} internal fun NotOnlineImageFromFile.toJceData(): ImMsgBody.NotOnlineImage { return ImMsgBody.NotOnlineImage( @@ -36,6 +50,7 @@ internal fun CustomFaceFromFile.toJceData(): ImMsgBody.CustomFace { height = this.height, source = this.source, size = this.size, + origin = this.origin, pbReserve = this.pbReserve ) } @@ -116,9 +131,7 @@ internal fun MessageChain.toRichTextElems(): MutableList { this.forEach { when (it) { is PlainText -> elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) - is At -> { - - } + is At -> elements.add(ImMsgBody.Elem(text = it.toJceData())) is CustomFaceFromFile -> elements.add(ImMsgBody.Elem(customFace = it.toJceData())) is CustomFaceFromServer -> elements.add(ImMsgBody.Elem(customFace = it.delegate)) is NotOnlineImageFromServer -> elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) @@ -152,33 +165,25 @@ internal class CustomFaceFromServer( override val height: Int get() = delegate.height override val source: Int get() = delegate.source override val size: Int get() = delegate.size + override val origin: Int get() = delegate.origin override val pbReserve: ByteArray get() = delegate.pbReserve } internal class NotOnlineImageFromServer( internal val delegate: ImMsgBody.NotOnlineImage ) : NotOnlineImage() { - 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 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 } +@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) internal fun ImMsgBody.RichText.toMessageChain(): MessageChain { val message = MessageChain(initialCapacity = elems.size) @@ -186,12 +191,17 @@ internal fun ImMsgBody.RichText.toMessageChain(): MessageChain { when { it.notOnlineImage != null -> message.add(NotOnlineImageFromServer(it.notOnlineImage)) it.customFace != null -> message.add(CustomFaceFromServer(it.customFace)) - it.text != null -> message.add(it.text.str.toMessage()) + it.text != null -> { + if (it.text.attr6Buf.isEmpty()) { + message.add(it.text.str.toMessage()) + } else { + //00 01 00 00 00 0A 00 3E 03 3F A2 00 00 + val id = it.text.attr6Buf.read { discardExact(7); readUInt().toLong() } + message.add(At(id, it.text.str)) + } + } } } return message -} - - -internal inline class ImageLinkQQA(override val original: String) : ImageLink \ No newline at end of file +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt index be4a6b6e4..10450a1ee 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt @@ -6,6 +6,7 @@ import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Group +import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.QQ import net.mamoe.mirai.data.EventPacket import net.mamoe.mirai.event.events.BotEvent @@ -19,6 +20,7 @@ import kotlin.jvm.JvmName @UseExperimental(MiraiInternalAPI::class) expect abstract class MessagePacket(bot: Bot) : MessagePacketBase +@Suppress("NOTHING_TO_INLINE") @MiraiInternalAPI abstract class MessagePacketBase(_bot: Bot) : EventPacket, BotEvent() { override val bot: Bot by _bot.unsafeWeakRef() @@ -69,6 +71,8 @@ abstract class MessagePacketBase(_bot: Bot) : suspend inline fun Message.send() = this.sendTo(subject) suspend inline fun String.send() = this.toMessage().sendTo(subject) + inline fun QQ.at(): At = At(this as Member) + // endregion // region Image download diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt index 457f91cbd..7675bf102 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt @@ -2,16 +2,18 @@ package net.mamoe.mirai.message.data -import net.mamoe.mirai.contact.QQ +import net.mamoe.mirai.contact.Member +import net.mamoe.mirai.utils.MiraiInternalAPI /** * At 一个人. 只能发送给一个群. */ -inline class At(val target: Long) : Message { - constructor(target: QQ) : this(target.id) +class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message { + @UseExperimental(MiraiInternalAPI::class) + constructor(member: Member) : this(member.id, member.groupCard) - override fun toString(): String = "[@$target]" // TODO: 2019/11/25 使用群名称进行 at. 因为手机端只会显示这个文字 + override fun toString(): String = display companion object Key : Message.Key @@ -24,4 +26,4 @@ inline class At(val target: Long) : Message { * At 这个成员 */ @Suppress("NOTHING_TO_INLINE") -inline fun QQ.at(): At = At(this) \ No newline at end of file +inline fun Member.at(): At = At(this) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt index 73cbfa60e..bc2a12484 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt @@ -31,6 +31,7 @@ abstract class CustomFace : Image() { abstract val source: Int abstract val size:Int abstract val pbReserve: ByteArray + abstract val origin: Int override fun toString(): String { return "[CustomFace]" @@ -57,6 +58,7 @@ data class CustomFaceFromFile( override val height: Int, override val source: Int, override val size: Int, + override val origin: Int, override val pbReserve: ByteArray ) : CustomFace() { override fun equals(other: Any?): Boolean { @@ -79,6 +81,7 @@ data class CustomFaceFromFile( if (height != other.height) return false if (source != other.source) return false if (size != other.size) return false + if (origin != this.origin) return false if (!pbReserve.contentEquals(other.pbReserve)) return false return true @@ -114,6 +117,9 @@ abstract class NotOnlineImage : Image() { open val bizType: Int get() = 0 open val imageType: Int get() = 1000 open val downloadPath: String get() = resourceId + open val origin: Int get()= 1 + open val signature: ByteArray get() = md5 + open val fileType: Int get()= 66 override fun toString(): String { return "[NotOnlineImage $resourceId]" diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt index 8824c85c5..171f232b9 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/ExternalImage.kt @@ -39,7 +39,7 @@ class ExternalImage( ): ExternalImage = ExternalImage(width, height, md5, format, data, data.remaining, filename) } - private val format: String = when (val it =imageFormat.toLowerCase()) { + private val format: String = when (val it = imageFormat.toLowerCase()) { "jpeg" -> "jpg" //必须转换 else -> it } @@ -56,7 +56,7 @@ class ExternalImage( * SHARPP: 1004 */ val imageType: Int - get() = when (format){ + get() = when (format) { "jpg" -> 1000 "png" -> 1001 "webp" -> 1002 diff --git a/mirai-demos/mirai-demo-gentleman/build.gradle b/mirai-demos/mirai-demo-gentleman/build.gradle index 8aa4702ff..a3fd4fcd6 100644 --- a/mirai-demos/mirai-demo-gentleman/build.gradle +++ b/mirai-demos/mirai-demo-gentleman/build.gradle @@ -1,6 +1,7 @@ apply plugin: "kotlin" apply plugin: "java" apply plugin: "application" +apply plugin: "kotlinx-serialization" dependencies { implementation files("../../mirai-core-qqandroid/build/classes/kotlin/jvm/main") // IDE bug diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt index 787850a25..57bd60613 100644 --- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt +++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt @@ -9,6 +9,7 @@ import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import net.mamoe.mirai.Bot import net.mamoe.mirai.alsoLogin +import net.mamoe.mirai.contact.Member import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.event.Subscribable import net.mamoe.mirai.event.events.ReceiveFriendAddRequestEvent @@ -67,7 +68,7 @@ suspend fun main() { always { } - case("at me") { At(sender).reply() } + case("at me") { At(sender as Member).reply() } // 等同于 "at me" reply { At(sender) } "你好" reply "你好!" diff --git a/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt b/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt index 1c275a97c..537a478da 100644 --- a/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt +++ b/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt @@ -5,7 +5,6 @@ import kotlinx.coroutines.GlobalScope import net.mamoe.mirai.event.events.BotLoginSucceedEvent import net.mamoe.mirai.event.subscribeAlways import net.mamoe.mirai.event.subscribeMessages -import net.mamoe.mirai.message.data.At import net.mamoe.mirai.plugin.PluginBase import net.mamoe.mirai.utils.MiraiExperimentalAPI @@ -19,7 +18,7 @@ class ImageSenderMain : PluginBase() { this.bot.subscribeMessages { case("at me") { - (At(sender) + " ? ").reply() + reply(sender.at() + " ? ") } (contains("image") or contains("图")) {