From d9e5805255130a29334e24f15cac266343c3f0c0 Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Mon, 3 Feb 2020 01:59:41 +0800
Subject: [PATCH] Improve performance

---
 .../net/mamoe/mirai/qqandroid/ContactImpl.kt  | 25 +++++----
 .../net/mamoe/mirai/qqandroid/QQAndroidBot.kt | 13 ++++-
 .../network/QQAndroidBotNetworkHandler.kt     | 16 +++---
 .../network/protocol/data/jce/TroopList.kt    |  6 +--
 .../packet/chat/receive/MessageSvc.kt         |  3 ++
 .../chat/receive/OnlinePush.PbPushGroupMsg.kt | 54 +++++++++++++------
 .../mamoe/mirai/qqandroid/utils/MessageQQA.kt |  5 +-
 .../commonMain/kotlin/net.mamoe.mirai/Bot.kt  | 30 +++++++----
 .../net.mamoe.mirai/contact/ContactList.kt    |  2 +-
 .../kotlin/net.mamoe.mirai/contact/Group.kt   | 17 +++---
 .../event/MessageSubscribers.kt               | 19 ++++++-
 .../net.mamoe.mirai/message/GroupMessage.kt   |  4 +-
 .../net.mamoe.mirai/utils/Exceptions.kt       | 22 --------
 .../net/mamoe/mirai/japt/BlockingGroup.java   | 17 ------
 .../japt/internal/BlockingContactsImpl.kt     |  5 +-
 15 files changed, 129 insertions(+), 109 deletions(-)
 delete mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt

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 8426a2963..8aab9c5e8 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
@@ -7,9 +7,11 @@ import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive.MessageSvc
-import net.mamoe.mirai.utils.*
+import net.mamoe.mirai.utils.ExternalImage
+import net.mamoe.mirai.utils.MiraiInternalAPI
+import net.mamoe.mirai.utils.getValue
+import net.mamoe.mirai.utils.unsafeWeakRef
 import kotlin.coroutines.CoroutineContext
-import kotlin.coroutines.EmptyCoroutineContext
 
 internal abstract class ContactImpl : Contact
 
@@ -82,19 +84,20 @@ internal class GroupImpl(
 ) : ContactImpl(), Group {
     override lateinit var owner: Member
 
-    override fun getMember(id: Long): Member =
-        members.delegate.filterGetOrNull { it.id == id } ?: error("Failed to find Member${id} in group ${groupCode}")
-
-    override suspend fun updateGroupInfo(): net.mamoe.mirai.data.GroupInfo {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-    }
-
     override suspend fun quit(): Boolean {
         TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
     }
 
-    operator fun get(key: Long): Member? {
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+    override operator fun get(id: Long): Member {
+        return members.delegate.filteringGetOrNull { it.id == id } ?: throw NoSuchElementException("for group id $id")
+    }
+
+    override fun contains(id: Long): Boolean {
+        return members.delegate.filteringGetOrNull { it.id == id } != null
+    }
+
+    override fun getOrNull(id: Long): Member? {
+        return members.delegate.filteringGetOrNull { it.id == id }
     }
 
     override val bot: QQAndroidBot by bot.unsafeWeakRef()
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
index 71c823dad..7a941c706 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.kt
@@ -12,7 +12,6 @@ import net.mamoe.mirai.utils.BotConfiguration
 import net.mamoe.mirai.utils.Context
 import net.mamoe.mirai.utils.LockFreeLinkedList
 import net.mamoe.mirai.utils.MiraiInternalAPI
-import net.mamoe.mirai.utils.io.getRandomByteArray
 import kotlin.coroutines.CoroutineContext
 
 @UseExperimental(MiraiInternalAPI::class)
@@ -32,10 +31,20 @@ internal abstract class QQAndroidBotBase constructor(
     override val uin: Long get() = client.uin
     override val qqs: ContactList<QQ> = ContactList(LockFreeLinkedList())
 
+    val selfQQ: QQ by lazy { QQ(uin) }
+
     override fun getQQ(id: Long): QQ {
         return qqs.delegate.filteringGetOrAdd({ it.id == id }, { QQImpl(this as QQAndroidBot, coroutineContext, id) })
     }
 
+    fun getQQOrAdd(id: Long): QQ {
+        return qqs.delegate.filteringGetOrAdd({ it.id == id }, { QQImpl(this as QQAndroidBot, coroutineContext, id) })
+    }
+
+    override fun QQ(id: Long): QQ {
+        return QQImpl(this as QQAndroidBot, coroutineContext, id)
+    }
+
     override fun createNetworkHandler(coroutineContext: CoroutineContext): QQAndroidBotNetworkHandler {
         return QQAndroidBotNetworkHandler(this as QQAndroidBot)
     }
@@ -47,7 +56,7 @@ internal abstract class QQAndroidBotBase constructor(
     }
 
     override fun getGroupByGroupCode(groupCode: Long): Group {
-        return groups.delegate.filterGetOrNull { it.groupCode == groupCode }
+        return groups.delegate.filteringGetOrNull { it.groupCode == groupCode }
             ?: throw NoSuchElementException("Can not found group with GroupCode=${groupCode}")
     }
 
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 7b348d689..230b9206a 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
@@ -10,7 +10,6 @@ import kotlinx.io.core.Input
 import kotlinx.io.core.buildPacket
 import kotlinx.io.core.use
 import net.mamoe.mirai.contact.ContactList
-import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.data.MultiPacket
@@ -30,7 +29,6 @@ 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.*
-import net.mamoe.mirai.utils.cryptor.contentToString
 import net.mamoe.mirai.utils.io.*
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
@@ -139,7 +137,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
                 totalFriendCount = data.totalFriendCount
                 data.friendList.forEach {
                     // atomic add
-                    bot.qqs.delegate.addLast(QQImpl(bot, EmptyCoroutineContext, it.friendUin).also {
+                    bot.qqs.delegate.addLast(bot.getQQ(it.friendUin).also {
                         currentFriendCount++
                     })
                 }
@@ -168,18 +166,18 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
                 val group =
                     GroupImpl(
                         bot,
-                        EmptyCoroutineContext,
+                        this.coroutineContext,
                         it.groupUin,
                         it.groupCode,
-                        it.groupName!!,
-                        it.groupMemo!!,
+                        it.groupName,
+                        it.groupMemo,
                         contactList
                     )
                 group.owner =
                     MemberImpl(
-                        QQImpl(bot, EmptyCoroutineContext, it.dwGroupOwnerUin!!),
+                        bot.QQ(it.dwGroupOwnerUin) as QQImpl,
                         group,
-                        EmptyCoroutineContext,
+                        group.coroutineContext,
                         MemberPermission.OWNER
                     )
                 toGet[group] = contactList
@@ -240,7 +238,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
                 if (it.memberUin != bot.uin) {
                     list.delegate.addLast(
                         MemberImpl(
-                            QQImpl(bot, EmptyCoroutineContext, it.memberUin),
+                            bot.QQ(it.memberUin) as QQImpl,
                             group,
                             EmptyCoroutineContext,
                             when {
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 266cd0920..c195bdbb1 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
@@ -47,8 +47,8 @@ internal class stTroopNum(
     @SerialId(1) val groupCode: Long,
     @SerialId(2) val flag: Byte? = null,
     @SerialId(3) val dwGroupInfoSeq: Long? = null,
-    @SerialId(4) val groupName: String? = "",
-    @SerialId(5) val groupMemo: String? = "",
+    @SerialId(4) val groupName: String = "",
+    @SerialId(5) val groupMemo: String = "",
     @SerialId(6) val dwGroupFlagExt: Long? = null,
     @SerialId(7) val dwGroupRankSeq: Long? = null,
     @SerialId(8) val dwCertificationType: Long? = null,
@@ -66,7 +66,7 @@ internal class stTroopNum(
     @SerialId(20) val dwMemberNumSeq: Long? = null,
     @SerialId(21) val dwMemberCardSeq: Long? = null,
     @SerialId(22) val dwGroupFlagExt3: Long? = null,
-    @SerialId(23) val dwGroupOwnerUin: Long? = null,
+    @SerialId(23) val dwGroupOwnerUin: Long,
     @SerialId(24) val isConfGroup: Byte? = null,
     @SerialId(25) val isModifyConfGroupFace: Byte? = null,
     @SerialId(26) val isModifyConfGroupName: Byte? = null,
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 98a54f1b3..34c37fd6c 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
@@ -135,6 +135,9 @@ internal class MessageSvc {
                 }
             }.toMutableList()
             if (resp.syncFlag == MsgSvc.SyncFlag.STOP) {
+                messages.ifEmpty {
+                    return GetMsgSuccess(messages)
+                }
                 return GetMsgSuccess(mutableListOf(messages.last()))
             }
             return Response(resp.syncFlag, messages)
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 b304e304d..2f2b5db3a 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
@@ -4,46 +4,68 @@ package net.mamoe.mirai.qqandroid.network.protocol.packet.chat.receive
 
 import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.contact.MemberPermission
+import net.mamoe.mirai.data.Packet
+import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.message.GroupMessage
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 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
+import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.qqandroid.utils.toMessageChain
 
+internal inline class GroupMessageOrNull(val delegate: GroupMessage?) : Packet {
+    override fun toString(): String {
+        return delegate?.toString() ?: "<Receipt>"
+    }
+}
 
 internal class OnlinePush {
+
     /**
      * 接受群消息
      */
-    internal object PbPushGroupMsg : IncomingPacketFactory<GroupMessage>("OnlinePush.PbPushGroupMsg") {
+    internal object PbPushGroupMsg : IncomingPacketFactory<GroupMessageOrNull>("OnlinePush.PbPushGroupMsg") {
         @UseExperimental(ExperimentalStdlibApi::class)
-        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessage {
+        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): GroupMessageOrNull {
             // 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 = readProtoBuf(MsgOnlinePush.PbPushMsg.serializer())
 
             val extraInfo: ImMsgBody.ExtraInfo? = pbPushMsg.msg.msgBody.richText.elems.firstOrNull { it.extraInfo != null }?.extraInfo
 
+            if (pbPushMsg.msg.msgHead.fromUin == bot.uin) {
+                return GroupMessageOrNull(null)
+            }
+
             val group = bot.getGroupByGroupCode(pbPushMsg.msg.msgHead.groupInfo!!.groupCode)
 
             val flags = extraInfo?.flags ?: 0
-            return GroupMessage(
-                bot = bot,
-                group = group,
-                senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
-                sender = group.getMember(pbPushMsg.msg.msgHead.fromUin),
-                message = pbPushMsg.msg.msgBody.richText.toMessageChain(),
-                permission = when {
-                    flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
-                    flags and 8 != 0 -> MemberPermission.OWNER
-                    flags == 0 -> MemberPermission.MEMBER
-                    else -> {
-                        bot.logger.warning("判断群员权限失败")
-                        MemberPermission.MEMBER
+            return GroupMessageOrNull(
+                GroupMessage(
+                    bot = bot,
+                    group = group,
+                    senderName = pbPushMsg.msg.msgHead.groupInfo.groupCard,
+                    sender = group[pbPushMsg.msg.msgHead.fromUin],
+                    message = pbPushMsg.msg.msgBody.richText.toMessageChain(),
+                    permission = when {
+                        flags and 16 != 0 -> MemberPermission.ADMINISTRATOR
+                        flags and 8 != 0 -> MemberPermission.OWNER
+                        flags == 0 -> MemberPermission.MEMBER
+                        else -> {
+                            bot.logger.warning("判断群员权限失败")
+                            MemberPermission.MEMBER
+                        }
                     }
-                }
+                )
             )
         }
+
+        override suspend fun QQAndroidBot.handle(packet: GroupMessageOrNull, sequenceId: Int): OutgoingPacket? {
+            if (packet.delegate != null) {
+                packet.delegate.broadcast()
+            }
+            return null
+        }
     }
 }
\ No newline at end of file
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 b9f842dd3..f6d5ff9fc 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
@@ -3,7 +3,6 @@ package net.mamoe.mirai.qqandroid.utils
 import net.mamoe.mirai.data.ImageLink
 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(
@@ -66,11 +65,11 @@ internal fun MessageChain.toRichTextElems(): MutableList<ImMsgBody.Elem> {
             }
             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())))
+             //   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())))
+                //  elements.add(ImMsgBody.Elem(generalFlags = ImMsgBody.GeneralFlags(pbReserve = "78 00 90 01 01 F8 01 00 A0 02 00 C8 02 00".hexToBytes())))
             }
         }
     }
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 409512892..5736a424c 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -3,17 +3,21 @@
 package net.mamoe.mirai
 
 import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
 import kotlinx.io.OutputStream
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.use
-import net.mamoe.mirai.contact.*
+import net.mamoe.mirai.contact.Contact
+import net.mamoe.mirai.contact.ContactList
+import net.mamoe.mirai.contact.Group
+import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.network.BotNetworkHandler
-import net.mamoe.mirai.utils.GroupNotFoundException
 import net.mamoe.mirai.utils.LoginFailedException
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.MiraiLogger
+import net.mamoe.mirai.utils.WeakRef
 import net.mamoe.mirai.utils.io.transferTo
 
 /**
@@ -49,32 +53,39 @@ abstract class Bot : CoroutineScope {
     // region contacts
 
     /**
-     * 与这个机器人相关的 QQ 列表. 机器人与 QQ 不一定是好友
+     * 机器人的好友列表.
      */
     abstract val qqs: ContactList<QQ>
 
     /**
-     * 获取缓存的 QQ 对象. 若没有对应的缓存, 则会线程安全地创建一个.
+     * 获取一个好友对象. 若没有这个好友, 则会抛出异常[NoSuchElementException]
      */
     abstract fun getQQ(id: Long): QQ
 
     /**
-     * 与这个机器人相关的群列表. 机器人不一定是群成员.
+     * 构造一个 [QQ] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
+     *
+     * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
+     * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
+     */
+    abstract fun QQ(id: Long): QQ
+
+    /**
+     * 机器人加入的群列表.
      */
     abstract val groups: ContactList<Group>
 
     /**
-     * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
-     * 若 [id] 无效, 将会抛出 [GroupNotFoundException]
+     * 获取一个机器人加入的群. 若没有这个群, 则会抛出异常 [NoSuchElementException]
      */
     abstract fun getGroupByID(id: Long): Group
 
     /**
-     * 获取缓存的群对象. 若没有对应的缓存, 则会线程安全地创建一个.
-     * 若 [internalId] 无效, 将会抛出 [GroupNotFoundException]
+     * 获取一个机器人加入的群. 若没有这个群, 则会抛出异常 [NoSuchElementException]
      */
     abstract fun getGroupByGroupCode(groupCode: Long): Group
 
+    // 目前还不能构造群对象. 这将在以后支持
 
     // endregion
 
@@ -87,6 +98,7 @@ abstract class Bot : CoroutineScope {
 
     /**
      * 登录, 或重新登录.
+     * 不建议调用这个函数.
      *
      * 最终调用 [net.mamoe.mirai.network.BotNetworkHandler.login]
      *
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 4156953ff..3e96f57eb 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
@@ -45,7 +45,7 @@ fun <C : Contact> LockFreeLinkedList<C>.getOrNull(id: Long): C? {
     return null
 }
 
-inline fun <C : Contact> LockFreeLinkedList<C>.filterGetOrNull(filter: (C) -> Boolean): C? {
+inline fun <C : Contact> LockFreeLinkedList<C>.filteringGetOrNull(filter: (C) -> Boolean): C? {
     forEach { if (filter(it)) return it }
     return null
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
index 9120fd5ca..450672bfb 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
@@ -3,8 +3,6 @@
 package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
-import net.mamoe.mirai.data.GroupInfo
-import net.mamoe.mirai.utils.coerceAtLeastOrFail
 
 
 /**
@@ -47,16 +45,19 @@ interface Group : Contact, CoroutineScope {
 
 
     /**
-     * 获取群成员. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
+     * 获取群成员实例. 若此 ID 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
      */
-    fun getMember(id: Long): Member
+    operator fun get(id: Long): Member
 
     /**
-     * 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新.
-     *
-     * @return 这一时刻的群资料
+     * 获取群成员实例, 不存在则 null
      */
-    suspend fun updateGroupInfo(): GroupInfo
+    fun getOrNull(id: Long): Member?
+
+    /**
+     * 检查此 id 的群成员是否存在
+     */
+    operator fun contains(id: Long): Boolean
 
     /**
      * 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
index 087274c0c..8d8222d0f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
@@ -144,7 +144,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
     /**
      * 监听的条件
      */
-    inner class ListeningFilter(
+    open inner class ListeningFilter(
         val filter: T.(String) -> Boolean
     ) {
         /**
@@ -345,6 +345,21 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
     inline fun sentBy(qq: Long, crossinline onEvent: MessageListener<T>): Listener<T> =
         content({ this.sender.id == qq }, onEvent)
 
+    /**
+     * 如果是好友发来的消息
+     */
+    @MessageDsl
+    inline fun sentByFriend(crossinline onEvent: MessageListener<FriendMessage>): Listener<T> =
+        content({ this is FriendMessage }){
+            onEvent(this as FriendMessage, it)
+        }
+
+    /**
+     * 如果是好友发来的消息
+     */
+    @MessageDsl
+    fun sentByFriend(): ListeningFilter = ListeningFilter { this is FriendMessage }
+
     /**
      * 如果是管理员或群主发的消息
      */
@@ -399,7 +414,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
      */
     @MessageDsl
     inline fun sentFrom(groupId: Long, crossinline onEvent: MessageListener<GroupMessage>): Listener<T> =
-        content({ this is GroupMessage && this.group.id == groupId }){
+        content({ this is GroupMessage && this.group.id == groupId }) {
             onEvent(this as GroupMessage, it)
         }
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
index a87465c55..f265fdc63 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/GroupMessage.kt
@@ -30,8 +30,8 @@ class GroupMessage(
      */
     override val subject: Group get() = group
 
-    inline fun At.member(): Member = group.getMember(this.target)
-    inline fun Long.member(): Member = group.getMember(this)
+    inline fun At.member(): Member = group[this.target]
+    inline fun Long.member(): Member = group[this]
     override fun toString(): String =
         "GroupMessage(group=${group.id}, senderName=$senderName, sender=${sender.id}, permission=${permission.name}, message=$message)"
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt
deleted file mode 100644
index b42004faa..000000000
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/Exceptions.kt
+++ /dev/null
@@ -1,22 +0,0 @@
-@file:Suppress("unused", "UNUSED_PARAMETER")
-
-package net.mamoe.mirai.utils
-
-import net.mamoe.mirai.contact.Group
-
-/**
- * 在获取 [Group] 对象等操作时可能出现的异常
- */
-class GroupNotFoundException : Exception {
-    constructor()
-    constructor(message: String?)
-    constructor(message: String?, cause: Throwable?)
-    constructor(cause: Throwable?)
-}
-
-open class MiraiInternalException : Exception {
-    constructor()
-    constructor(message: String?)
-    constructor(message: String?, cause: Throwable?)
-    constructor(cause: Throwable?)
-}
\ No newline at end of file
diff --git a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java
index e40d96de3..54f50e0f5 100644
--- a/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java
+++ b/mirai-japt/src/main/java/net/mamoe/mirai/japt/BlockingGroup.java
@@ -1,7 +1,6 @@
 package net.mamoe.mirai.japt;
 
 import net.mamoe.mirai.contact.Group;
-import net.mamoe.mirai.data.GroupInfo;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.Map;
@@ -9,28 +8,20 @@ import java.util.NoSuchElementException;
 
 @SuppressWarnings("unused")
 public interface BlockingGroup extends BlockingContact {
-    /**
-     * 内部 ID
-     */
-    long getInternalId();
-
     /**
      * 群主 (同步事件更新)
-     * 进行 {@link #updateGroupInfo} 时将会更新这个值.
      */
     @NotNull
     BlockingMember getOwner();
 
     /**
      * 群名称 (同步事件更新)
-     * 进行 {@link #updateGroupInfo} 时将会更新这个值.
      */
     @NotNull
     String getName();
 
     /**
      * 入群公告, 没有时为空字符串. (同步事件更新)
-     * 进行 {@link #updateGroupInfo} 时将会更新这个值.
      */
     @NotNull
     String getAnnouncement();
@@ -49,14 +40,6 @@ public interface BlockingGroup extends BlockingContact {
     @NotNull
     BlockingMember getMember(long id);
 
-    /**
-     * 更新群资料. 群资料会与服务器事件同步事件更新, 一般情况下不需要手动更新.
-     *
-     * @return 这一时刻的群资料
-     */
-    @NotNull
-    GroupInfo updateGroupInfo();
-
     /**
      * 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
      */
diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
index 295d9fffe..f13cae01c 100644
--- a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
+++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
@@ -8,7 +8,6 @@ import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.data.FriendNameRemark
-import net.mamoe.mirai.data.GroupInfo
 import net.mamoe.mirai.data.PreviousNameList
 import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.japt.BlockingBot
@@ -41,16 +40,14 @@ internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup {
     override fun getOwner(): BlockingMember = delegate.owner.blocking()
     override fun getName(): String = delegate.name
     override fun getId(): Long = delegate.id
-    override fun updateGroupInfo(): GroupInfo = runBlocking { delegate.updateGroupInfo() }
     override fun toFullString(): String = delegate.toFullString()
-    override fun getMember(id: Long): BlockingMember = delegate.getMember(id).blocking()
+    override fun getMember(id: Long): BlockingMember = delegate[id].blocking()
     override fun getBot(): BlockingBot = delegate.bot.blocking()
     override fun getAnnouncement(): String = delegate.announcement
     @UseExperimental(MiraiInternalAPI::class)
     override fun getMembers(): Map<Long, BlockingMember> =
         delegate.members.delegate.toList().associateBy { it.id }.mapValues { it.value.blocking() }
 
-    override fun getInternalId(): Long = delegate.internalId.value
     override fun quit(): Boolean = runBlocking { delegate.quit() }
 }