From 5d46d1c4be8179b481dc8a1c7585d20932f64936 Mon Sep 17 00:00:00 2001 From: Him188 Date: Sun, 2 Feb 2020 23:21:42 +0800 Subject: [PATCH] C2C message send support --- .../net/mamoe/mirai/qqandroid/QQAndroidBot.kt | 5 +- .../network/QQAndroidBotNetworkHandler.kt | 8 +- .../network/protocol/data/proto/MsgCommon.kt | 2 +- .../network/protocol/data/proto/SyncCookie.kt | 9 +- .../packet/chat/receive/MessageSvc.kt | 39 +++--- .../chat/receive/OnlinePush.PbPushGroupMsg.kt | 5 +- .../mamoe/mirai/qqandroid/utils/MessageQQA.kt | 130 +++++++++++++----- .../commonMain/kotlin/net.mamoe.mirai/Bot.kt | 7 +- .../kotlin/net.mamoe.mirai/contact/Contact.kt | 2 +- .../net.mamoe.mirai/contact/ContactList.kt | 2 +- .../net.mamoe.mirai/message/MessagePacket.kt | 8 +- 11 files changed, 134 insertions(+), 83 deletions(-) diff --git a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt index 927b314e0..c4ade482d 100644 --- a/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt +++ b/mirai-core-qqandroid/src/androidMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt @@ -3,8 +3,11 @@ package net.mamoe.mirai.qqandroid import net.mamoe.mirai.BotAccount import net.mamoe.mirai.utils.BotConfiguration import net.mamoe.mirai.utils.Context +import net.mamoe.mirai.utils.MiraiInternalAPI -internal actual class QQAndroidBot actual constructor( +@UseExperimental(MiraiInternalAPI::class) +internal actual class QQAndroidBot +actual constructor( context: Context, account: BotAccount, configuration: BotConfiguration diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt index f50153147..4077d3a1d 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/QQAndroidBotNetworkHandler.kt @@ -18,16 +18,15 @@ import net.mamoe.mirai.qqandroid.QQAndroidBot import net.mamoe.mirai.qqandroid.QQImpl import net.mamoe.mirai.qqandroid.event.ForceOfflineEvent import net.mamoe.mirai.qqandroid.event.PacketReceivedEvent +import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.* +import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc import net.mamoe.mirai.qqandroid.network.protocol.packet.list.FriendList import net.mamoe.mirai.qqandroid.network.protocol.packet.login.LoginPacket import net.mamoe.mirai.qqandroid.network.protocol.packet.login.StatSvc -import net.mamoe.mirai.utils.LockFreeLinkedList -import net.mamoe.mirai.utils.MiraiInternalAPI +import net.mamoe.mirai.utils.* import net.mamoe.mirai.utils.cryptor.contentToString -import net.mamoe.mirai.utils.getValue import net.mamoe.mirai.utils.io.* -import net.mamoe.mirai.utils.unsafeWeakRef import kotlin.coroutines.CoroutineContext import kotlin.coroutines.EmptyCoroutineContext import kotlin.jvm.Volatile @@ -107,6 +106,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler override suspend fun init() { // delay(5000) + MessageSvc.PbGetMsg(bot.client, MsgSvc.SyncFlag.START, currentTimeSeconds).sendWithoutExpect() this@QQAndroidBotNetworkHandler.subscribeAlways { if (this@QQAndroidBotNetworkHandler.bot == this.bot) { diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgCommon.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgCommon.kt index b7286f5f9..1826af132 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgCommon.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/MsgCommon.kt @@ -145,7 +145,7 @@ internal class MsgComm : ProtoBuf { @SerialId(1) val lastReadTime: Int = 0, @SerialId(2) val peerUin: Long = 0L, @SerialId(3) val msgCompleted: Int = 0, - @SerialId(4) val msg: List, + @SerialId(4) val msg: List? = null, @SerialId(5) val unreadMsgNum: Int = 0, @SerialId(8) val c2cType: Int = 0, @SerialId(9) val serviceType: Int = 0, diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt index 6a0411386..f39695cc3 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/SyncCookie.kt @@ -3,14 +3,15 @@ package net.mamoe.mirai.qqandroid.network.protocol.data.proto import kotlinx.serialization.SerialId import kotlinx.serialization.Serializable import net.mamoe.mirai.qqandroid.io.ProtoBuf +import kotlin.math.absoluteValue import kotlin.random.Random @Serializable class SyncCookie( @SerialId(1) val time1: Long? = null, // 1580277992 @SerialId(2) val time: Long, // 1580277992 - @SerialId(3) val unknown1: Long = Random.nextLong(),// 678328038 - @SerialId(4) val unknown2: Long = Random.nextLong(), // 1687142153 + @SerialId(3) val unknown1: Long = Random.nextLong().absoluteValue,// 678328038 + @SerialId(4) val unknown2: Long = Random.nextLong().absoluteValue, // 1687142153 @SerialId(5) val const1: Long = const1_, // 1458467940 @SerialId(11) val const2: Long = const2_, // 2683038258 @SerialId(12) val unknown3: Long = 0x1d, @@ -18,8 +19,8 @@ class SyncCookie( @SerialId(14) val unknown4: Long = 0 ) : ProtoBuf -private val const1_: Long = Random.nextLong() -private val const2_: Long = Random.nextLong() +private val const1_: Long = Random.nextLong().absoluteValue +private val const2_: Long = Random.nextLong().absoluteValue /* @Serializable diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index 825010b16..98a54f1b3 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -4,6 +4,7 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.discardExact import net.mamoe.mirai.data.MultiPacket import net.mamoe.mirai.data.Packet +import net.mamoe.mirai.event.BroadcastControllable import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.data.MessageChain import net.mamoe.mirai.qqandroid.QQAndroidBot @@ -29,7 +30,6 @@ import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.cryptor.contentToString import net.mamoe.mirai.utils.currentTimeSeconds import net.mamoe.mirai.utils.io.hexToBytes -import net.mamoe.mirai.utils.io.toUHexString import kotlin.math.absoluteValue import kotlin.random.Random @@ -39,14 +39,14 @@ internal class MessageSvc { */ internal object PushNotify : IncomingPacketFactory("MessageSvc.PushNotify") { override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): RequestPushNotify { - discardExact(4) + discardExact(4) // don't remove return decodeUniPacket(RequestPushNotify.serializer()) } override suspend fun QQAndroidBot.handle(packet: RequestPushNotify, sequenceId: Int): OutgoingPacket? { network.run { - return PbGetMsg(client, MsgSvc.SyncFlag.START, packet.stMsgInfo?.uMsgTime ?: 0) + return PbGetMsg(client, MsgSvc.SyncFlag.START, packet.stMsgInfo?.uMsgTime ?: currentTimeSeconds) } } } @@ -57,9 +57,6 @@ internal class MessageSvc { */ @UseExperimental(MiraiInternalAPI::class) internal object PbGetMsg : OutgoingPacketFactory("MessageSvc.PbGetMsg") { - val EXTRA_DATA = - "08 00 12 33 6D 6F 64 65 6C 3A 78 69 67 6F 6D 69 20 36 3B 6F 73 3A 32 32 3B 76 65 72 73 69 6F 6E 3A 76 32 6D 61 6E 3A 78 69 61 6F 6D 69 73 79 73 3A 4C 4D 59 34 38 5A 18 E4 E1 A4 FF FE 2D 20 E9 E1 A4 FF FE 2D 28 A8 E1 A4 FF FE 2D 30 99 E1 A4 FF FE 2D".hexToBytes() - operator fun invoke( client: QQAndroidClient, syncFlag: MsgSvc.SyncFlag = MsgSvc.SyncFlag.START, @@ -67,7 +64,7 @@ internal class MessageSvc { ): OutgoingPacket = buildOutgoingUniPacket( client ) { - println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}") + //println("syncCookie=${client.c2cMessageSync.syncCookie?.toUHexString()}") writeProtoBuf( MsgSvc.PbGetMsgReq.serializer(), MsgSvc.PbGetMsgReq( @@ -96,7 +93,11 @@ internal class MessageSvc { * 不要直接 expect 这个 class. 它可能 */ @MiraiInternalAPI - open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: MutableList) : MultiPacket(delegate) { + open class Response(internal val syncFlagFromServer: MsgSvc.SyncFlag, delegate: MutableList) : MultiPacket(delegate), + BroadcastControllable { + override val shouldBroadcast: Boolean + get() = syncFlagFromServer == MsgSvc.SyncFlag.STOP + override fun toString(): String { return "MessageSvc.PbGetMsg.Response($syncFlagFromServer=$syncFlagFromServer, messages=List(size=${this.size}))" } @@ -120,7 +121,7 @@ internal class MessageSvc { return GetMsgSuccess(mutableListOf()) } - val messages = resp.uinPairMsgs.asSequence().flatMap { it.msg.asSequence() }.mapNotNull { + val messages = resp.uinPairMsgs.asSequence().filterNot { it.msg == null }.flatMap { it.msg!!.asSequence() }.mapNotNull { when (it.msgHead.msgType) { 166 -> { FriendMessage( @@ -134,7 +135,7 @@ internal class MessageSvc { } }.toMutableList() if (resp.syncFlag == MsgSvc.SyncFlag.STOP) { - return GetMsgSuccess(messages) + return GetMsgSuccess(mutableListOf(messages.last())) } return Response(resp.syncFlag, messages) } @@ -146,7 +147,7 @@ internal class MessageSvc { MsgSvc.SyncFlag.CONTINUE -> { network.run { - PbGetMsg(client, MsgSvc.SyncFlag.CONTINUE, currentTimeSeconds) + PbGetMsg(client, MsgSvc.SyncFlag.CONTINUE, currentTimeSeconds).sendWithoutExpect() } return } @@ -199,8 +200,7 @@ internal class MessageSvc { ), msgSeq = client.atomicNextMessageSequenceId(), msgRand = Random.nextInt().absoluteValue, - syncCookie = client.c2cMessageSync.syncCookie?.takeIf { it.isNotEmpty() } - ?: SyncCookie(time = currentTimeSeconds).toByteArray(SyncCookie.serializer()) + syncCookie = SyncCookie(time = currentTimeSeconds).toByteArray(SyncCookie.serializer()) // msgVia = 1 ) ) @@ -217,21 +217,22 @@ internal class MessageSvc { ///writeFully("0A 08 0A 06 08 89 FC A6 8C 0B 12 06 08 01 10 00 18 00 1A 1F 0A 1D 12 08 0A 06 0A 04 F0 9F 92 A9 12 11 AA 02 0E 88 01 00 9A 01 08 78 00 F8 01 00 C8 02 00 20 9B 7A 28 F4 CA 9B B8 03 32 34 08 92 C2 C4 F1 05 10 92 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 89 84 F9 A2 06 48 DE 8C EA E5 0E 58 D9 BD BB A0 09 60 1D 68 92 C2 C4 F1 05 70 00 40 01".hexToBytes()) + val seq = client.atomicNextMessageSequenceId() ///return@buildOutgoingUniPacket writeProtoBuf( MsgSvc.PbSendMsgReq.serializer(), MsgSvc.PbSendMsgReq( routingHead = MsgSvc.RoutingHead(grp = MsgSvc.Grp(groupCode = groupId)), // TODO: 2020/1/30 确认这里是 id 还是 internalId - contentHead = MsgComm.ContentHead(pkgNum = 1), + contentHead = MsgComm.ContentHead(pkgNum = 1, divSeq = seq), msgBody = ImMsgBody.MsgBody( richText = ImMsgBody.RichText( elems = message.toRichTextElems() ) ), - msgSeq = client.atomicNextMessageSequenceId(), - //msgRand = Random.nextInt() and 0x7FFF, - syncCookie = SyncCookie(time = currentTimeSeconds).toByteArray(SyncCookie.serializer()) - //SyncCookie(currentTimeSeconds, Random.nextLong().absoluteValue, Random.nextLong().absoluteValue).toByteArray(SyncCookie.serializer()) - // msgVia = 1 + msgSeq = seq, + msgRand = Random.nextInt().absoluteValue, + syncCookie = "08 A0 C2 C4 F1 05 10 A0 C2 C4 F1 05 18 E6 ED B9 C3 02 20 89 FE BE A4 06 28 E4 C2 B1 95 03 48 A1 9F E0 C7 08 58 D3 C2 8F A0 09 60 1D 68 A0 C2 C4 F1 05 70 00".hexToBytes() + ?: SyncCookie(time = currentTimeSeconds + client.timeDifference).toByteArray(SyncCookie.serializer()), + msgVia = 0 ) ) } 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 3503b1192..67d03a3b2 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 @@ -3,11 +3,10 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive import kotlinx.io.core.ByteReadPacket -import kotlinx.io.core.readBytes import net.mamoe.mirai.contact.MemberPermission import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.qqandroid.QQAndroidBot -import net.mamoe.mirai.qqandroid.io.serialization.ProtoBufWithNullableSupport +import net.mamoe.mirai.qqandroid.io.serialization.readProtoBuf import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.qqandroid.network.protocol.data.proto.MsgOnlinePush import net.mamoe.mirai.qqandroid.network.protocol.packet.IncomingPacketFactory @@ -22,7 +21,7 @@ internal class OnlinePush { @UseExperimental(ExperimentalStdlibApi::class) override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessage { // 00 00 02 E4 0A D5 05 0A 4F 08 A2 FF 8C F0 03 10 DD F1 92 B7 07 18 52 20 00 28 BC 3D 30 8C 82 AB F1 05 38 D2 80 E0 8C 80 80 80 80 02 4A 21 08 E7 C1 AD B8 02 10 01 18 BA 05 22 09 48 69 6D 31 38 38 6D 6F 65 30 06 38 02 42 05 4D 69 72 61 69 50 01 58 01 60 00 88 01 08 12 06 08 01 10 00 18 00 1A F9 04 0A F6 04 0A 26 08 00 10 87 82 AB F1 05 18 B7 B4 BF 30 20 00 28 0C 30 00 38 86 01 40 22 4A 0C E5 BE AE E8 BD AF E9 9B 85 E9 BB 91 12 E6 03 42 E3 03 12 2A 7B 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 22 00 2A 04 03 00 00 00 32 60 15 36 20 39 36 6B 45 31 41 38 35 32 32 39 64 63 36 39 38 34 37 39 37 37 62 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 34 45 31 38 35 38 32 32 2D 30 45 37 42 2D 46 38 30 46 2D 43 35 42 31 2D 33 34 34 38 38 33 37 34 44 33 39 43 7D 2E 6A 70 67 31 32 31 32 41 38 C6 BB 8A A9 08 40 FB AE 9E C2 09 48 50 50 41 5A 00 60 01 6A 10 4E 18 58 22 0E 7B F8 0F C5 B1 34 48 83 74 D3 9C 72 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 31 39 38 3F 74 65 72 6D 3D 32 82 01 57 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 30 3F 74 65 72 6D 3D 32 B0 01 4D B8 01 2E C8 01 FF 05 D8 01 4D E0 01 2E FA 01 59 2F 67 63 68 61 74 70 69 63 5F 6E 65 77 2F 31 30 34 30 34 30 30 32 39 30 2F 36 35 35 30 35 37 31 32 37 2D 32 32 33 33 36 33 38 33 34 32 2D 34 45 31 38 35 38 32 32 30 45 37 42 46 38 30 46 43 35 42 31 33 34 34 38 38 33 37 34 44 33 39 43 2F 34 30 30 3F 74 65 72 6D 3D 32 80 02 4D 88 02 2E 12 45 AA 02 42 50 03 60 00 68 00 9A 01 39 08 09 20 BF 50 80 01 01 C8 01 00 F0 01 00 F8 01 00 90 02 00 98 03 00 A0 03 20 B0 03 00 C0 03 00 D0 03 00 E8 03 00 8A 04 04 08 02 08 01 90 04 80 80 80 10 B8 04 00 C0 04 00 12 06 4A 04 08 00 40 01 12 14 82 01 11 0A 09 48 69 6D 31 38 38 6D 6F 65 18 06 20 08 28 03 10 8A CA 9D A1 07 1A 00 - val pbPushMsg = ProtoBufWithNullableSupport.load(MsgOnlinePush.PbPushMsg.serializer(), readBytes()) + val pbPushMsg = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer()) val extraInfo: ImMsgBody.ExtraInfo? = pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo 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 0bda9be52..b9f842dd3 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 @@ -5,62 +5,123 @@ import net.mamoe.mirai.message.data.* import net.mamoe.mirai.qqandroid.network.protocol.data.proto.ImMsgBody import net.mamoe.mirai.utils.io.hexToBytes +internal fun NotOnlineImageFromFile.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 + ) +} +/* +notOnlineImage=NotOnlineImage#2050019814 { + filePath=41AEF2D4B5BD24CF3791EFC5FEB67D60.jpg + fileLen=0x00000350(848) + downloadPath=/f2b7e5c0-acb3-4e83-aa5c-c8383840cc91 + oldVerSendFile= + imgType=0x000003E8(1000) + previewsImage= + picMd5=41 AE F2 D4 B5 BD 24 CF 37 91 EF C5 FE B6 7D 60 + picHeight=0x00000032(50) + picWidth=0x00000033(51) + resId=/f2b7e5c0-acb3-4e83-aa5c-c8383840cc91 + flag= + thumbUrl= + original=0x00000000(0) + bigUrl= + origUrl= + bizType=0x00000005(5) + result=0x00000000(0) + index=0x00000000(0) + opFaceBuf= + oldPicMd5=false + thumbWidth=0x00000000(0) + thumbHeight=0x00000000(0) + fileId=0x00000000(0) + showLen=0x00000000(0) + downloadLen=0x00000000(0) + _400Url= + _400Width=0x00000000(0) + _400Height=0x00000000(0) + pbReserve=08 01 10 00 32 00 42 0E 5B E5 8A A8 E7 94 BB E8 A1 A8 E6 83 85 5D 50 00 78 05 +} + */ internal fun MessageChain.toRichTextElems(): MutableList { - val elems = mutableListOf() + val elements = mutableListOf() this.forEach { when (it) { is PlainText -> { - elems.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) + elements.add(ImMsgBody.Elem(text = ImMsgBody.Text(str = it.stringValue))) } is At -> { } - is Image -> { - elems.add( - ImMsgBody.Elem( - notOnlineImage = ImMsgBody.NotOnlineImage( - filePath = it.id.value, // 错了, 应该是 2B23D705CAD1F2CF3710FE582692FCC4.jpg - fileLen = 1149, // 假的 - downloadPath = it.id.value, - imgType = 1000, // 不确定 - picMd5 = "2B 23 D7 05 CA D1 F2 CF 37 10 FE 58 26 92 FC C4".hexToBytes(), - picHeight = 66, - picWidth = 66, - resId = it.id.value, - bizType = 5, - pbReserve = ImMsgBody.PbReserve.DEFAULT // 可能还可以改变 `[动画表情]` - ) - ) - ) + is NotOnlineImageFromServer -> { + elements.add(ImMsgBody.Elem(notOnlineImage = it.delegate)) + elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()))) + } + is NotOnlineImageFromFile -> { + elements.add(ImMsgBody.Elem(notOnlineImage = it.toJceData())) + elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes()))) } } } - return elems + return elements } +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 + +} + internal fun ImMsgBody.RichText.toMessageChain(): MessageChain { val message = MessageChain(initialCapacity = elems.size) elems.forEach { when { it.notOnlineImage != null -> message.add( - Image( - ImageIdQQA( - it.notOnlineImage.resId, - it.notOnlineImage.origUrl - ) - ) + NotOnlineImageFromServer(it.notOnlineImage) ) it.customFace != null -> message.add( - Image( - ImageIdQQA( - it.customFace.filePath, - it.customFace.origUrl - ) + NotOnlineImageFromFile( + it.customFace.filePath, + it.customFace.md5, + it.customFace.origUrl, + it.customFace.downloadLen, + it.customFace.height, + it.customFace.width, + it.customFace.bizType, + it.customFace.imageType, + it.customFace.filePath ) ) it.text != null -> message.add(it.text.str.toMessage()) @@ -70,12 +131,5 @@ internal fun ImMsgBody.RichText.toMessageChain(): MessageChain { return message } -internal class ImageIdQQA( - override val value: String, - originalLink: String -) : ImageId { - val link: ImageLink = - ImageLinkQQA("http://gchat.qpic.cn$originalLink") -} internal inline class ImageLinkQQA(override val original: String) : ImageLink \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt index 889564387..417c682ec 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt @@ -8,7 +8,6 @@ import kotlinx.io.core.ByteReadPacket import kotlinx.io.core.use import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.AddFriendResult -import net.mamoe.mirai.data.ImageLink import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.network.BotNetworkHandler import net.mamoe.mirai.utils.GroupNotFoundException @@ -104,11 +103,9 @@ abstract class Bot : CoroutineScope { // region actions - abstract suspend fun Image.getLink(): ImageLink + abstract suspend fun Image.downloadAsByteArray(): ByteArray - suspend fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray() - - suspend fun Image.download(): ByteReadPacket = getLink().download() + abstract suspend fun Image.download(): ByteReadPacket /** * 添加一个好友 diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index b712fa29e..c200aef27 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -36,7 +36,7 @@ interface Contact : CoroutineScope { */ suspend fun sendMessage(message: MessageChain) - suspend fun uploadImage(image: ExternalImage): ImageId + suspend fun uploadImage(image: ExternalImage): Image } suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.toChain()) diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt index f725d1d56..2b1365daa 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactList.kt @@ -37,7 +37,7 @@ class ContactList(@MiraiInternalAPI val delegate: LockFreeLinkedLis operator fun LockFreeLinkedList.get(id: Long): C { forEach { if (it.id == id) return it } - throw NoSuchElementException() + throw NoSuchElementException("No such contact with id $id") } fun LockFreeLinkedList.getOrNull(id: Long): C? { 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 94679ab57..721b57942 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,7 +6,6 @@ import kotlinx.io.core.ByteReadPacket import net.mamoe.mirai.Bot import net.mamoe.mirai.contact.* import net.mamoe.mirai.data.EventPacket -import net.mamoe.mirai.data.ImageLink import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.message.data.* import net.mamoe.mirai.utils.* @@ -65,17 +64,14 @@ abstract class MessagePacketBase(_bot: Bot) : suspend inline fun ExternalImage.upload(): Image = this.upload(subject) suspend inline fun Image.send() = this.sendTo(subject) - suspend inline fun ImageId.send() = this.sendTo(subject) suspend inline fun Message.send() = this.sendTo(subject) suspend inline fun String.send() = this.toMessage().sendTo(subject) // endregion // region Image download - suspend inline fun Image.getLink(): ImageLink = with(bot) { getLink() } - - suspend inline fun Image.downloadAsByteArray(): ByteArray = getLink().downloadAsByteArray() - suspend inline fun Image.download(): ByteReadPacket = getLink().download() + suspend inline fun Image.downloadAsByteArray(): ByteArray = bot.run { downloadAsByteArray() } + suspend inline fun Image.download(): ByteReadPacket = bot.run { download() } // endregion fun At.qq(): QQ = bot.getQQ(this.target)