diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
index c54e2e62b..a2ef510a9 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/QQAndroidBot.common.kt
@@ -38,8 +38,8 @@ import net.mamoe.mirai.event.events.NewFriendRequestEvent
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.network.LoginFailedException
+import net.mamoe.mirai.qqandroid.contact.FriendImpl
 import net.mamoe.mirai.qqandroid.contact.MemberInfoImpl
-import net.mamoe.mirai.qqandroid.contact.QQImpl
 import net.mamoe.mirai.qqandroid.contact.checkIsGroupImpl
 import net.mamoe.mirai.qqandroid.message.*
 import net.mamoe.mirai.qqandroid.network.QQAndroidBotNetworkHandler
@@ -89,7 +89,7 @@ internal class QQAndroidBot constructor(
                 event,
                 accept = true
             ).sendWithoutExpect()
-            bot.friends.delegate.addLast(bot._lowLevelNewQQ(object : FriendInfo {
+            bot.friends.delegate.addLast(bot._lowLevelNewFriend(object : FriendInfo {
                 override val uin: Long get() = event.fromId
                 override val nick: String get() = event.fromNick
             }))
@@ -150,7 +150,6 @@ internal class QQAndroidBot constructor(
     }
 
     override suspend fun ignoreMemberJoinRequest(event: MemberJoinRequestEvent, blackList: Boolean) {
-
         check(event.responded.compareAndSet(false, true)) {
             "the request $this has already been responded"
         }
@@ -188,15 +187,15 @@ internal abstract class QQAndroidBotBase constructor(
         val json = Json(JsonConfiguration(ignoreUnknownKeys = true, encodeDefaults = true))
     }
 
-    override val friends: ContactList<QQ> = ContactList(LockFreeLinkedList())
+    override val friends: ContactList<Friend> = ContactList(LockFreeLinkedList())
 
     override val nick: String get() = selfInfo.nick
 
     internal lateinit var selfInfo: JceFriendInfo
 
-    override val selfQQ: QQ by lazy {
+    override val selfQQ: Friend by lazy {
         @OptIn(LowLevelAPI::class)
-        _lowLevelNewQQ(object : FriendInfo {
+        _lowLevelNewFriend(object : FriendInfo {
             override val uin: Long get() = this@QQAndroidBotBase.id
             override val nick: String get() = this@QQAndroidBotBase.nick
         })
@@ -214,10 +213,10 @@ internal abstract class QQAndroidBotBase constructor(
     }
 
     @LowLevelAPI
-    override fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ {
-        return QQImpl(
+    override fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend {
+        return FriendImpl(
             this as QQAndroidBot,
-            coroutineContext + CoroutineName("QQ(${friendInfo.uin}"),
+            coroutineContext + CoroutineName("Friend(${friendInfo.uin}"),
             friendInfo.uin,
             friendInfo
         )
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt
similarity index 77%
rename from mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
rename to mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt
index 13dae8893..46efcef23 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/QQImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/FriendImpl.kt
@@ -8,7 +8,7 @@
  */
 
 @file:OptIn(MiraiInternalAPI::class, LowLevelAPI::class)
-@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR")
+@file:Suppress("EXPERIMENTAL_API_USAGE", "DEPRECATION_ERROR", "NOTHING_TO_INLINE")
 
 package net.mamoe.mirai.qqandroid.contact
 
@@ -16,12 +16,8 @@ import kotlinx.atomicfu.AtomicInt
 import kotlinx.atomicfu.atomic
 import kotlinx.io.core.Closeable
 import net.mamoe.mirai.LowLevelAPI
-import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.contact.QQ
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.data.FriendInfo
-import net.mamoe.mirai.data.FriendNameRemark
-import net.mamoe.mirai.data.PreviousNameList
-import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.BeforeImageUploadEvent
 import net.mamoe.mirai.event.events.EventCancelledException
@@ -35,7 +31,10 @@ import net.mamoe.mirai.qqandroid.network.protocol.data.proto.Cmd0x352
 import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.image.LongConn
 import net.mamoe.mirai.qqandroid.utils.MiraiPlatformUtils
 import net.mamoe.mirai.qqandroid.utils.toUHexString
-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.contracts.ExperimentalContracts
 import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
@@ -49,20 +48,20 @@ internal inline class FriendInfoImpl(
 }
 
 @OptIn(ExperimentalContracts::class)
-internal fun QQ.checkIsQQImpl(): QQImpl {
+internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
     contract {
-        returns() implies (this@checkIsQQImpl is QQImpl)
+        returns() implies (this@checkIsFriendImpl is FriendImpl)
     }
-    check(this is QQImpl) { "A QQ instance is not instance of QQImpl. Don't interlace two protocol implementations together!" }
+    check(this is FriendImpl) { "A Friend instance is not instance of FriendImpl. Don't interlace two protocol implementations together!" }
     return this
 }
 
-internal class QQImpl(
+internal class FriendImpl(
     bot: QQAndroidBot,
     override val coroutineContext: CoroutineContext,
     override val id: Long,
     private val friendInfo: FriendInfo
-) : QQ() {
+) : Friend() {
     @Suppress("unused") // bug
     val lastMessageSequence: AtomicInt = atomic(-1)
 
@@ -72,7 +71,7 @@ internal class QQImpl(
 
     @JvmSynthetic
     @Suppress("DuplicatedCode")
-    override suspend fun sendMessage(message: Message): MessageReceipt<QQ> {
+    override suspend fun sendMessage(message: Message): MessageReceipt<Friend> {
         return sendMessageImpl(message).also {
             logMessageSent(message)
         }
@@ -110,7 +109,7 @@ internal class QQImpl(
                     width = response.imageInfo.fileWidth,
                     resourceId = response.resourceId
                 ).also {
-                    ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
+                    ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
                 }
                 is LongConn.OffPicUp.Response.RequireUpload -> {
                     MiraiPlatformUtils.Http.postImage(
@@ -141,11 +140,11 @@ internal class QQImpl(
                         width = image.width,
                         resourceId = response.resourceId
                     ).also {
-                        ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
+                        ImageUploadEvent.Succeed(this@FriendImpl, image, it).broadcast()
                     }
                 }
                 is LongConn.OffPicUp.Response.Failed -> {
-                    ImageUploadEvent.Failed(this@QQImpl, image, -1, response.message).broadcast()
+                    ImageUploadEvent.Failed(this@FriendImpl, image, -1, response.message).broadcast()
                     error(response.message)
                 }
             }
@@ -153,35 +152,4 @@ internal class QQImpl(
     } finally {
         (image.input as? Closeable)?.close()
     }
-
-    override fun hashCode(): Int {
-        var result = bot.hashCode()
-        result = 31 * result + id.hashCode()
-        return result
-    }
-
-    override fun equals(other: Any?): Boolean {
-        @Suppress("DuplicatedCode")
-        if (this === other) return true
-        if (other !is Contact) return false
-        if (this::class != other::class) return false
-        return this.id == other.id && this.bot == other.bot
-    }
-
-    @MiraiExperimentalAPI
-    override suspend fun queryProfile(): Profile {
-        TODO("not implemented")
-    }
-
-    @MiraiExperimentalAPI
-    override suspend fun queryPreviousNameList(): PreviousNameList {
-        TODO("not implemented")
-    }
-
-    @MiraiExperimentalAPI
-    override suspend fun queryRemark(): FriendNameRemark {
-        TODO("not implemented")
-    }
-
-    override fun toString(): String = "QQ($id)"
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
index 8c86774da..faf25a19b 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/GroupImpl.kt
@@ -250,7 +250,7 @@ internal class GroupImpl(
     override fun newMember(memberInfo: MemberInfo): Member {
         return MemberImpl(
             @OptIn(LowLevelAPI::class)
-            bot._lowLevelNewQQ(memberInfo) as QQImpl,
+            bot._lowLevelNewFriend(memberInfo) as FriendImpl,
             this,
             this.coroutineContext,
             memberInfo
@@ -476,23 +476,5 @@ internal class GroupImpl(
         (image.input as? Closeable)?.close()
     }
 
-    override fun toString(): String {
-        return "Group($id)"
-    }
-
-    override fun hashCode(): Int {
-        var result = bot.hashCode()
-        result = 31 * result + id.hashCode()
-        return result
-    }
-
-    override fun equals(other: Any?): Boolean {
-        @Suppress("DuplicatedCode", "DuplicatedCode")
-        if (this === other) return true
-        if (other !is Contact) return false
-        if (this::class != other::class) return false
-        return this.id == other.id && this.bot == other.bot
-    }
-
-
+    override fun toString(): String = "Group($id)"
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
index b6062d7df..4fc9335f4 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/MemberImpl.kt
@@ -16,10 +16,7 @@ import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.launch
 import net.mamoe.mirai.LowLevelAPI
 import net.mamoe.mirai.contact.*
-import net.mamoe.mirai.data.FriendNameRemark
 import net.mamoe.mirai.data.MemberInfo
-import net.mamoe.mirai.data.PreviousNameList
-import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.MemberCardChangeEvent
 import net.mamoe.mirai.event.events.MemberLeaveEvent
@@ -42,7 +39,7 @@ import kotlin.jvm.JvmSynthetic
 @OptIn(LowLevelAPI::class)
 @Suppress("MemberVisibilityCanBePrivate")
 internal class MemberImpl constructor(
-    val qq: QQImpl, // 不要 WeakRef
+    val qq: FriendImpl, // 不要 WeakRef
     group: GroupImpl,
     override val coroutineContext: CoroutineContext,
     memberInfo: MemberInfo
@@ -52,22 +49,10 @@ internal class MemberImpl constructor(
     @Suppress("unused") // false positive
     val lastMessageSequence: AtomicInt = atomic(-1)
 
-    // region QQ delegate
     override val id: Long = qq.id
     override val nick: String = qq.nick
 
-    @MiraiExperimentalAPI
-    override suspend fun queryProfile(): Profile = qq.queryProfile()
-
-    @MiraiExperimentalAPI
-    override suspend fun queryPreviousNameList(): PreviousNameList = qq.queryPreviousNameList()
-
-    @MiraiExperimentalAPI
-    override suspend fun queryRemark(): FriendNameRemark = qq.queryRemark()
-
-    @OptIn(MiraiInternalAPI::class)
     @JvmSynthetic
-    @Suppress("DuplicatedCode", "UNCHECKED_CAST")
     override suspend fun sendMessage(message: Message): MessageReceipt<Member> {
         return sendMessageImpl(message).also {
             logMessageSent(message)
@@ -92,7 +77,6 @@ internal class MemberImpl constructor(
 
     @JvmSynthetic
     override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage = qq.uploadImage(image)
-    // endregion
 
     override var permission: MemberPermission = memberInfo.permission
 
@@ -217,23 +201,6 @@ internal class MemberImpl constructor(
             MemberLeaveEvent.Kick(this@MemberImpl, null).broadcast()
         }
     }
-
-    override fun hashCode(): Int {
-        var result = bot.hashCode()
-        result = 31 * result + id.hashCode()
-        return result
-    }
-
-    override fun equals(other: Any?): Boolean {
-        if (this === other) return true
-        if (other !is Contact) return false
-        if (this::class != other::class) return false
-        return this.id == other.id && this.bot == other.bot
-    }
-
-    override fun toString(): String {
-        return "Member($id)"
-    }
 }
 
 @OptIn(ExperimentalContracts::class)
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
index 2fc27621f..cf68a81de 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/contact/util.kt
@@ -10,7 +10,7 @@
 package net.mamoe.mirai.qqandroid.contact
 
 import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.contact.QQ
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.EventCancelledException
 import net.mamoe.mirai.event.events.MessageSendEvent
@@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.verbose
 
 @OptIn(MiraiInternalAPI::class)
-internal suspend fun QQ.sendMessageImpl(message: Message): MessageReceipt<QQ> {
+internal suspend fun Friend.sendMessageImpl(message: Message): MessageReceipt<Friend> {
     val event = MessageSendEvent.FriendMessageSendEvent(this, message.asMessageChain()).broadcast()
     if (event.isCancelled) {
         throw EventCancelledException("cancelled by FriendMessageSendEvent")
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/incomingSourceImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/incomingSourceImpl.kt
index b8507ffcb..3ee4741b6 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/incomingSourceImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/incomingSourceImpl.kt
@@ -12,8 +12,8 @@
 package net.mamoe.mirai.qqandroid.message
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
@@ -57,7 +57,7 @@ internal class MessageSourceFromFriendImpl(
     override val random: Int get() = msg.msgBody.richText.attr!!.random
     override val time: Int get() = msg.msgHead.msgTime
     override val originalMessage: MessageChain by lazy { msg.toMessageChain(bot, 0, false) }
-    override val sender: QQ get() = bot.getFriend(msg.msgHead.fromUin)
+    override val sender: Friend get() = bot.getFriend(msg.msgHead.fromUin)
 
     private val jceData by lazy { msg.toJceDataFriendOrTemp(random) }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
index 13b0939e7..832ec4eff 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/outgoingSourceImpl.kt
@@ -14,9 +14,9 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.Deferred
 import kotlinx.coroutines.ExperimentalCoroutinesApi
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.event.asyncFromEventOrNull
 import net.mamoe.mirai.event.internal.MiraiAtomicBoolean
 import net.mamoe.mirai.message.data.MessageChain
@@ -74,7 +74,7 @@ internal class MessageSourceToFriendImpl(
     override val time: Int,
     override val originalMessage: MessageChain,
     override val sender: Bot,
-    override val target: QQ
+    override val target: Friend
 ) : OnlineMessageSource.Outgoing.ToFriend(), MessageSourceInternal {
     override val bot: Bot
         get() = sender
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 92af20e90..40c03b08d 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
@@ -227,7 +227,7 @@ internal class QQAndroidBotNetworkHandler(bot: QQAndroidBot) : BotNetworkHandler
             data.friendList.forEach {
                 // atomic
                 bot.friends.delegate.addLast(
-                    QQImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))
+                    FriendImpl(bot, bot.coroutineContext, it.friendUin, FriendInfoImpl(it))
                 ).also { currentFriendCount++ }
             }
             logger.verbose { "正在加载好友列表 ${currentFriendCount}/${totalFriendCount}" }
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 95f228532..9a4562364 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
@@ -18,10 +18,10 @@ import kotlinx.coroutines.flow.*
 import kotlinx.io.core.ByteReadPacket
 import kotlinx.io.core.discardExact
 import net.mamoe.mirai.LowLevelAPI
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
-import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.data.MemberInfo
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.event.events.BotJoinGroupEvent
@@ -33,8 +33,8 @@ import net.mamoe.mirai.message.TempMessage
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.qqandroid.QQAndroidBot
 import net.mamoe.mirai.qqandroid.contact.GroupImpl
+import net.mamoe.mirai.qqandroid.contact.checkIsFriendImpl
 import net.mamoe.mirai.qqandroid.contact.checkIsMemberImpl
-import net.mamoe.mirai.qqandroid.contact.checkIsQQImpl
 import net.mamoe.mirai.qqandroid.message.*
 import net.mamoe.mirai.qqandroid.network.MultiPacketByIterable
 import net.mamoe.mirai.qqandroid.network.Packet
@@ -53,10 +53,8 @@ import net.mamoe.mirai.qqandroid.utils.io.serialization.readProtoBuf
 import net.mamoe.mirai.qqandroid.utils.io.serialization.readUniPacket
 import net.mamoe.mirai.qqandroid.utils.io.serialization.toByteArray
 import net.mamoe.mirai.qqandroid.utils.io.serialization.writeProtoBuf
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
-import net.mamoe.mirai.utils.MiraiInternalAPI
-import net.mamoe.mirai.utils.currentTimeSeconds
-import net.mamoe.mirai.utils.debug
+import net.mamoe.mirai.utils.*
+import kotlin.collections.firstOrNull
 import kotlin.math.absoluteValue
 import kotlin.random.Random
 
@@ -191,7 +189,7 @@ internal class MessageSvc {
 
             if (resp.result != 0) {
                 bot.network.logger
-                    .warning("MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}")
+                    .warning { "MessageSvc.PushNotify: result != 0, result = ${resp.result}, errorMsg=${resp.errmsg}" }
                 return EmptyResponse
             }
 
@@ -231,7 +229,7 @@ internal class MessageSvc {
                         }
                         166 -> {
                             val friend = bot.getFriendOrNull(msg.msgHead.fromUin) ?: return@mapNotNull null
-                            friend.checkIsQQImpl()
+                            friend.checkIsFriendImpl()
 
                             if (msg.msgHead.fromUin == bot.id || !bot.firstLoginSucceed) {
                                 return@mapNotNull null
@@ -365,7 +363,7 @@ internal class MessageSvc {
 
         inline fun createToFriend(
             client: QQAndroidClient,
-            qq: QQ,
+            qq: Friend,
             message: MessageChain,
             crossinline sourceCallback: (MessageSourceToFriendImpl) -> Unit
         ): OutgoingPacket {
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
index 6fd4a28a9..f2265008b 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
@@ -409,7 +409,7 @@ internal class OnlinePush {
             0xB3L to lambda528 { bot ->
                 // 08 01 12 52 08 A2 FF 8C F0 03 10 00 1D 15 3D 90 5E 22 2E E6 88 91 E4 BB AC E5 B7 B2 E7 BB 8F E6 98 AF E5 A5 BD E5 8F 8B E5 95 A6 EF BC 8C E4 B8 80 E8 B5 B7 E6 9D A5 E8 81 8A E5 A4 A9 E5 90 A7 21 2A 09 48 69 6D 31 38 38 6D 6F 65 30 07 38 03 48 DD F1 92 B7 07
                 val body = vProtobuf.loadAs(Submsgtype0xb3.SubMsgType0xb3.MsgBody.serializer())
-                val new = bot._lowLevelNewQQ(object : FriendInfo {
+                val new = bot._lowLevelNewFriend(object : FriendInfo {
                     override val uin: Long get() = body.msgAddFrdNotify.fuin
                     override val nick: String get() = body.msgAddFrdNotify.fuinNick
                 })
diff --git a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/QQ.kt b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/QQ.kt
index 093040808..63db3304f 100644
--- a/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/QQ.kt
+++ b/mirai-core/src/androidMain/kotlin/net/mamoe/mirai/contact/QQ.kt
@@ -28,8 +28,6 @@ import net.mamoe.mirai.utils.OverFileSizeMaxException
  *
  * A QQ instance helps you to receive event from or sendPacket event to.
  * Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
- *
- * @author Him188moe
  */
 @Suppress("INAPPLICABLE_JVM_NAME")
 actual abstract class QQ : Contact(), CoroutineScope {
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 d00de4f3a..a244fcf24 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/Bot.kt
@@ -109,20 +109,21 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
     // region contacts
 
     /**
-     * [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewQQ] 实例
+     * [QQ.id] 与 [Bot.uin] 相同的 [_lowLevelNewFriend] 实例
      */
-    abstract val selfQQ: QQ
+    abstract val selfQQ: Friend
+
 
     /**
      * 机器人的好友列表. 与服务器同步更新
      */
-    abstract val friends: ContactList<QQ>
+    abstract val friends: ContactList<Friend>
 
     /**
      * 获取一个好友对象.
      * @throws [NoSuchElementException] 当不存在这个好友时抛出
      */
-    fun getFriend(id: Long): QQ = friends.firstOrNull { it.id == id } ?: throw NoSuchElementException("friend $id")
+    fun getFriend(id: Long): Friend = friends.firstOrNull { it.id == id } ?: throw NoSuchElementException("friend $id")
 
     /**
      * 机器人加入的群列表. 与服务器同步更新
@@ -273,9 +274,17 @@ abstract class Bot : CoroutineScope, LowLevelBotAPIAccessor, BotJavaFriendlyAPI(
     @MiraiInternalAPI
     abstract val network: BotNetworkHandler
 
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
-    suspend inline fun Bot.join() = this.coroutineContext[Job]!!.join()
+    @PlannedRemoval("1.0.0.")
+    @get:JvmName("getSelfQQ")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    val selfQQDeprecated: QQ
+        get() = selfQQ
+
+    @JvmName("getFriend")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun getFriendDeprecated(id: Long): QQ = this.getFriend(id)
 }
 
 /**
@@ -351,7 +360,7 @@ inline fun Bot.containsFriend(id: Long): Boolean = this.friends.contains(id)
 inline fun Bot.containsGroup(id: Long): Boolean = this.groups.contains(id)
 
 @JvmSynthetic
-inline fun Bot.getFriendOrNull(id: Long): QQ? = this.friends.getOrNull(id)
+inline fun Bot.getFriendOrNull(id: Long): Friend? = this.friends.getOrNull(id)
 
 @JvmSynthetic
 inline fun Bot.getGroupOrNull(id: Long): Group? = this.groups.getOrNull(id)
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 b4c98884f..e407ce616 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
@@ -32,9 +32,7 @@ import kotlin.jvm.JvmSynthetic
 
 
 /**
- * 联系人. 虽然叫做联系人, 但他的子类有 [QQ], [群成员][Member] 和 [群][Group].
- *
- * @author Him188moe
+ * 联系人. 虽然叫做联系人, 但他的子类有 [人][User], 和 [群][Group].
  */
 abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot {
     /**
@@ -85,23 +83,11 @@ abstract class Contact : CoroutineScope, ContactJavaFriendlyAPI(), ContactOrBot
     @JvmSynthetic
     abstract suspend fun uploadImage(image: ExternalImage): OfflineImage
 
-    /**
-     * 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
-     *
-     * 注:
-     * [id] 相同的 [Member] 和 [QQ], 他们并不 [equals].
-     * 因为, [Member] 含义为群员, 必属于一个群.
-     * 而 [QQ] 含义为一个独立的人, 可以是好友, 也可以是陌生人.
-     */
-    abstract override fun equals(other: Any?): Boolean
+    final override fun equals(other: Any?): Boolean = super.equals(other)
+    final override fun hashCode(): Int = super.hashCode()
 
     /**
-     * @return `bot.hashCode() * 31 + id.hashCode()`
-     */
-    abstract override fun hashCode(): Int
-
-    /**
-     * @return "QQ($id)" or "Group($id)" or "Member($id)"
+     * @return "Friend($id)" or "Group($id)" or "Member($id)"
      */
     abstract override fun toString(): String
 }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactOrBot.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactOrBot.kt
index c9ce94d7e..cf6b570c2 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactOrBot.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/ContactOrBot.kt
@@ -15,12 +15,11 @@ import net.mamoe.mirai.utils.SinceMirai
 /**
  * 拥有 [id] 的对象.
  * 此为 [Contact] 与 [Bot] 的唯一公共接口.
- * **注意:** 此接口为实验性接口, 将来可能会发生不兼容的更名.
  *
  * @see Contact
  * @see Bot
  */
-@SinceMirai("0.37.2")
+@SinceMirai("0.39.0")
 interface ContactOrBot {
     /**
      * QQ 号或群号.
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt
new file mode 100644
index 000000000..8a616fb57
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Friend.kt
@@ -0,0 +1,80 @@
+/*
+ * 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("EXPERIMENTAL_API_USAGE", "unused")
+
+package net.mamoe.mirai.contact
+
+import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.events.EventCancelledException
+import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
+import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
+import net.mamoe.mirai.message.MessageReceipt
+import net.mamoe.mirai.message.data.Message
+import net.mamoe.mirai.message.data.toMessage
+import kotlin.jvm.JvmSynthetic
+
+/**
+ * 好友 对象.
+ * 注意: 一个 [Friend] 实例并不是独立的, 它属于一个 [Bot].
+ * 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
+ *
+ * 对于同一个 [Bot] 任何一个人的 [Friend] 实例都是单一的.
+ * 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
+ */
+@Suppress("DEPRECATION_ERROR")
+abstract class Friend : QQ(), CoroutineScope {
+    /**
+     * 请求头像下载链接
+     */
+    // @MiraiExperimentalAPI
+    //suspend fun queryAvatar(): AvatarLink
+    /**
+     * QQ 号码
+     */
+    abstract override val id: Long
+
+    /**
+     * 昵称
+     */
+    abstract override val nick: String
+
+    /**
+     * 头像下载链接
+     */
+    override val avatarUrl: String
+        get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
+
+    /**
+     * 向这个对象发送消息.
+     *
+     * 单条消息最大可发送 4500 字符或 50 张图片.
+     *
+     * @see FriendMessageSendEvent 发送好友信息事件, cancellable
+     * @see GroupMessageSendEvent  发送群消息事件. cancellable
+     *
+     * @throws EventCancelledException 当发送消息事件被取消时抛出
+     * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
+     * @throws MessageTooLargeException 当消息过长时抛出
+     *
+     * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
+     */
+    @JvmSynthetic
+    abstract override suspend fun sendMessage(message: Message): MessageReceipt<Friend>
+
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
+    @kotlin.internal.InlineOnly // purely virtual
+    @JvmSynthetic
+    suspend inline fun sendMessage(message: String): MessageReceipt<Friend> {
+        return sendMessage(message.toMessage())
+    }
+
+    final override fun toString(): String = "Friend($id)"
+}
\ No newline at end of file
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 b2a583839..3f876a254 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
@@ -21,23 +21,25 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.OfflineGroupImage
+import net.mamoe.mirai.message.data.toMessage
 import net.mamoe.mirai.utils.ExternalImage
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.OverFileSizeMaxException
 import net.mamoe.mirai.utils.SinceMirai
+import kotlin.jvm.JvmStatic
 import kotlin.jvm.JvmSynthetic
 
 /**
- * 群. 在 QQ Android 中叫做 "Troop"
+ * 群.
  */
-expect abstract class Group() : Contact, CoroutineScope {
+abstract class Group : Contact(), CoroutineScope {
     /**
      * 群名称.
      *
-     * 在修改时将会异步上传至服务器.
+     * 在修改时将会异步上传至服务器, 也会广播事件 [GroupNameChangeEvent].
      * 频繁修改可能会被服务器拒绝.
      *
-     * @see MemberPermissionChangeEvent
+     * @see GroupNameChangeEvent 群名片修改事件
      * @throws PermissionDeniedException 无权限修改时将会抛出异常
      */
     abstract var name: String
@@ -88,6 +90,7 @@ expect abstract class Group() : Contact, CoroutineScope {
      * 群头像下载链接.
      */
     val avatarUrl: String
+        get() = "https://p.qlogo.cn/gh/$id/${id}_1/640"
 
     /**
      * 群成员列表, 不含机器人自己, 含群主.
@@ -148,6 +151,16 @@ expect abstract class Group() : Contact, CoroutineScope {
     @JvmSynthetic
     abstract override suspend fun sendMessage(message: Message): MessageReceipt<Group>
 
+    /**
+     * @see sendMessage
+     */
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
+    @kotlin.internal.InlineOnly // purely virtual
+    @JvmSynthetic
+    suspend inline fun sendMessage(message: String): MessageReceipt<Group> {
+        return sendMessage(message.toMessage())
+    }
+
     /**
      * 上传一个图片以备发送.
      *
@@ -161,14 +174,20 @@ expect abstract class Group() : Contact, CoroutineScope {
     abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
 
     companion object {
-        // don't @JvmStatic: JDK 1.8 required
-        fun calculateGroupUinByGroupCode(groupCode: Long): Long
+        /**
+         * @suppress internal api
+         */
+        @JvmStatic
+        fun calculateGroupUinByGroupCode(groupCode: Long): Long =
+            CommonGroupCalculations.calculateGroupUinByGroupCode(groupCode)
 
-        fun calculateGroupCodeByGroupUin(groupUin: Long): Long
+        /**
+         * @suppress internal api
+         */
+        @JvmStatic
+        fun calculateGroupCodeByGroupUin(groupUin: Long): Long =
+            CommonGroupCalculations.calculateGroupCodeByGroupUin(groupUin)
     }
-
-    @MiraiExperimentalAPI
-    fun toFullString(): String
 }
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
index a6e5ada81..fb1c5d94d 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
@@ -14,8 +14,10 @@ package net.mamoe.mirai.contact
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.JavaFriendlyAPI
 import net.mamoe.mirai.event.events.*
+import net.mamoe.mirai.getFriendOrNull
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
+import net.mamoe.mirai.message.data.toMessage
 import net.mamoe.mirai.utils.MiraiInternalAPI
 import net.mamoe.mirai.utils.WeakRefProperty
 import kotlin.jvm.JvmSynthetic
@@ -24,10 +26,13 @@ import kotlin.time.ExperimentalTime
 
 /**
  * 群成员.
- */ // 不要删除多平台结构, kotlin bug
+ *
+ * 群成员可能也是好友, 但他们在对象类型上不同.
+ * 群成员可以通过 [asFriend] 得到相关好友对象.
+ */
 @Suppress("INAPPLICABLE_JVM_NAME")
 @OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
-expect abstract class Member() : MemberJavaFriendlyAPI {
+abstract class Member : MemberJavaFriendlyAPI() {
     /**
      * 所在的群.
      */
@@ -119,16 +124,6 @@ expect abstract class Member() : MemberJavaFriendlyAPI {
     @JvmSynthetic
     abstract suspend fun kick(message: String = "")
 
-    /**
-     * 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
-     */
-    abstract override fun equals(other: Any?): Boolean
-
-    /**
-     * @return `bot.hashCode() * 31 + id.hashCode()`
-     */
-    abstract override fun hashCode(): Int
-
     /**
      * 向这个对象发送消息.
      *
@@ -145,6 +140,43 @@ expect abstract class Member() : MemberJavaFriendlyAPI {
      */
     @JvmSynthetic
     abstract override suspend fun sendMessage(message: Message): MessageReceipt<Member>
+
+    /**
+     * @see sendMessage
+     */
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
+    @kotlin.internal.InlineOnly // purely virtual
+    @JvmSynthetic
+    suspend inline fun sendMessage(message: String): MessageReceipt<Member> {
+        return sendMessage(message.toMessage())
+    }
+
+    final override fun toString(): String = "Member($id)"
+}
+
+/**
+ * 得到此成员作为好友的对象.
+ */
+inline val Member.asFriend: Friend
+    get() = this.bot.getFriendOrNull(this.id) ?: error("$this is not a friend")
+
+/**
+ * 得到此成员作为好友的对象.
+ */
+inline val Member.asFriendOrNull: Friend?
+    get() = this.bot.getFriendOrNull(this.id)
+
+/**
+ * 判断此成员是否为好友
+ */
+inline val Member.isFriend: Boolean
+    get() = this.bot.friends.contains(this.id)
+
+/**
+ * 如果此成员是好友, 则执行 [block] 并返回其返回值. 否则返回 `null`
+ */
+inline fun <R> Member.takeIfFriend(block: (Friend) -> R): R? {
+    return this.asFriendOrNull?.let(block)
 }
 
 /**
@@ -161,6 +193,9 @@ fun Member.isMuted(): Boolean {
     return muteTimeRemaining != 0 && muteTimeRemaining != 0xFFFFFFFF.toInt()
 }
 
+/**
+ * @see Member.mute
+ */
 @ExperimentalTime
 suspend inline fun Member.mute(duration: Duration) {
     require(duration.inDays <= 30) { "duration must be at most 1 month" }
@@ -168,4 +203,7 @@ suspend inline fun Member.mute(duration: Duration) {
     this.mute(duration.inSeconds.toInt())
 }
 
+/**
+ * @see Member.mute
+ */
 suspend inline fun Member.mute(durationSeconds: Long) = this.mute(durationSeconds.toInt())
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/MemberPermission.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/MemberPermission.kt
index 4bb688768..80b4d3527 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/MemberPermission.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/MemberPermission.kt
@@ -12,10 +12,8 @@
 package net.mamoe.mirai.contact
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.SinceMirai
 
-
 /**
  * 群成员的权限.
  *
@@ -37,39 +35,42 @@ enum class MemberPermission : Comparable<MemberPermission> {
      */
     OWNER; // ordinal = 2
 
+    /**
+     * 权限等级. [OWNER] 为 2, [ADMINISTRATOR] 为 1, [MEMBER] 为 0
+     */
     @SinceMirai("0.32.0")
     val level: Int
         get() = ordinal
 }
 
 /**
- * 是群主
+ * 判断权限是否为群主
  */
 inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
 
 /**
- * 是管理员
+ * 判断权限是否为管理员
  */
 inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
 
 /**
- * 是管理员或群主
+ * 判断权限是否为管理员或群主
  */
 inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
 
 
 /**
- * 是群主
+ * 判断权限是否为群主
  */
 inline fun Member.isOwner(): Boolean = this.permission.isOwner()
 
 /**
- * 是管理员
+ * 判断权限是否为管理员
  */
 inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
 
 /**
- * 是管理员或群主
+ * 判断权限是否为管理员或群主
  */
 inline fun Member.isOperator(): Boolean = this.permission.isOperator()
 
@@ -77,9 +78,10 @@ inline fun Member.isOperator(): Boolean = this.permission.isOperator()
 /**
  * 权限不足
  */
-expect class PermissionDeniedException : IllegalStateException {
-    constructor()
-    constructor(message: String?)
+@Suppress("unused")
+class PermissionDeniedException : IllegalStateException {
+    constructor() : super("Permission denied")
+    constructor(message: String?) : super(message)
 }
 
 /**
@@ -87,10 +89,9 @@ expect class PermissionDeniedException : IllegalStateException {
  *
  * @throws PermissionDeniedException
  */
-@OptIn(MiraiExperimentalAPI::class)
 inline fun Group.checkBotPermission(
     required: MemberPermission,
-    lazyMessage: () -> String = {
+    crossinline lazyMessage: () -> String = {
         "Permission denied: required $required, got actual $botPermission for $bot in group $id"
     }
 ) {
@@ -104,9 +105,8 @@ inline fun Group.checkBotPermission(
  *
  * @throws PermissionDeniedException
  */
-@OptIn(MiraiExperimentalAPI::class)
 inline fun Group.checkBotPermissionOperator(
-    lazyMessage: () -> String = {
+    crossinline lazyMessage: () -> String = {
         "Permission denied: required ${MemberPermission.ADMINISTRATOR} or ${MemberPermission.OWNER}, got actual $botPermission for $bot in group $id"
     }
 ) {
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
index c54ad63fb..a5055deb8 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
@@ -13,76 +13,47 @@ package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.data.FriendNameRemark
-import net.mamoe.mirai.data.PreviousNameList
-import net.mamoe.mirai.data.Profile
-import net.mamoe.mirai.event.events.BeforeImageUploadEvent
 import net.mamoe.mirai.event.events.EventCancelledException
-import net.mamoe.mirai.event.events.ImageUploadEvent
 import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
 import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.OfflineFriendImage
-import net.mamoe.mirai.utils.ExternalImage
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
-import net.mamoe.mirai.utils.OverFileSizeMaxException
+import net.mamoe.mirai.utils.PlannedRemoval
 import kotlin.jvm.JvmSynthetic
 
 /**
  * QQ 对象.
- * 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
- * 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
  *
- * 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的.
+ * 自 0.39.0 起 mirai 引入 [User] 作为 [Friend] 和 [Member] 的父类,
+ * 以备将来支持仅 [Friend] 可用的 API, 如设置备注.
  *
- * A QQ instance helps you to receive event from or sendPacket event to.
- * Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
+ * 所有 API 均有二进制兼容.
  *
- * @author Him188moe
+ * 请根据实际情况, 使用 [Friend] 或 [User] 替代.
  */
-@Suppress("INAPPLICABLE_JVM_NAME")
-expect abstract class QQ() : Contact, CoroutineScope {
-    /**
-     * 请求头像下载链接
-     */
-    // @MiraiExperimentalAPI
-    //suspend fun queryAvatar(): AvatarLink
+@PlannedRemoval("1.0.0")
+@Deprecated(
+    "use Friend or Person instead",
+    replaceWith = ReplaceWith("Friend", "net.mamoe.mirai.contact.Friend"),
+    level = DeprecationLevel.ERROR
+)
+@Suppress("DEPRECATION_ERROR")
+abstract class QQ : User(), CoroutineScope {
     /**
      * QQ 号码
      */
     abstract override val id: Long
+
     /**
      * 昵称
      */
-    abstract val nick: String
-
-    /**
-     * 查询用户资料
-     */
-    @MiraiExperimentalAPI("还未支持")
-    abstract suspend fun queryProfile(): Profile
+    abstract override val nick: String
 
     /**
      * 头像下载链接
      */
-    val avatarUrl: String
-
-    /**
-     * 查询曾用名.
-     *
-     * 曾用名可能是:
-     * - 昵称
-     * - 共同群内的群名片
-     */
-    @MiraiExperimentalAPI("还未支持")
-    abstract suspend fun queryPreviousNameList(): PreviousNameList
-
-    /**
-     * 查询机器人账号给这个人设置的备注
-     */
-    @MiraiExperimentalAPI("还未支持")
-    abstract suspend fun queryRemark(): FriendNameRemark
+    override val avatarUrl: String
+        get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
 
     /**
      * 向这个对象发送消息.
@@ -100,16 +71,4 @@ expect abstract class QQ() : Contact, CoroutineScope {
      */
     @JvmSynthetic
     abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
-
-    /**
-     * 上传一个图片以备发送.
-     *
-     * @see BeforeImageUploadEvent 图片发送前事件, cancellable
-     * @see ImageUploadEvent 图片发送完成事件
-     *
-     * @throws EventCancelledException 当发送消息事件被取消
-     * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
-     */
-    @JvmSynthetic
-    abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
 }
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt
similarity index 52%
rename from mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/QQ.kt
rename to mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt
index 093040808..fd8456a7b 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/QQ.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/User.kt
@@ -1,12 +1,18 @@
-@file:Suppress("unused")
+/*
+ * 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("EXPERIMENTAL_API_USAGE", "unused")
 
 package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.data.FriendNameRemark
-import net.mamoe.mirai.data.PreviousNameList
-import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.event.events.BeforeImageUploadEvent
 import net.mamoe.mirai.event.events.EventCancelledException
 import net.mamoe.mirai.event.events.ImageUploadEvent
@@ -15,66 +21,38 @@ import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Message
 import net.mamoe.mirai.message.data.OfflineFriendImage
+import net.mamoe.mirai.message.data.toMessage
 import net.mamoe.mirai.utils.ExternalImage
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.OverFileSizeMaxException
+import kotlin.jvm.JvmSynthetic
 
 /**
- * QQ 对象.
- * 注意: 一个 [QQ] 实例并不是独立的, 它属于一个 [Bot].
- * 它不能被直接构造. 任何时候都应从 [Bot.getFriend] 或事件中获取.
+ * 代表一个 **用户**.
  *
- * 对于同一个 [Bot] 任何一个人的 [QQ] 实例都是单一的.
+ * 其子类有 [群成员][Member] 和 [好友][Friend].
+ * 虽然群成员也可能是好友, 但他们仍是不同的两个类型.
  *
- * A QQ instance helps you to receive event from or sendPacket event to.
- * Notice that, one QQ instance belong to one [Bot], that is, QQ instances from different [Bot] are NOT the same.
+ * 注意: 一个 [User] 实例并不是独立的, 它属于一个 [Bot].
  *
- * @author Him188moe
+ * 对于同一个 [Bot] 任何一个人的 [User] 实例都是单一的.
  */
-@Suppress("INAPPLICABLE_JVM_NAME")
-actual abstract class QQ : Contact(), CoroutineScope {
-    /**
-     * 请求头像下载链接
-     */
-    // @MiraiExperimentalAPI
-    //suspend fun queryAvatar(): AvatarLink
+abstract class User : Contact(), CoroutineScope {
     /**
      * QQ 号码
      */
-    actual abstract override val id: Long
+    abstract override val id: Long
+
     /**
      * 昵称
      */
-    actual abstract val nick: String
-
-    /**
-     * 查询用户资料
-     */
-    @MiraiExperimentalAPI("还未支持")
-    actual abstract suspend fun queryProfile(): Profile
+    abstract val nick: String
 
     /**
      * 头像下载链接
      */
-    actual val avatarUrl: String
+    open val avatarUrl: String
         get() = "http://q1.qlogo.cn/g?b=qq&nk=$id&s=640"
 
-    /**
-     * 查询曾用名.
-     *
-     * 曾用名可能是:
-     * - 昵称
-     * - 共同群内的群名片
-     */
-    @MiraiExperimentalAPI("还未支持")
-    actual abstract suspend fun queryPreviousNameList(): PreviousNameList
-
-    /**
-     * 查询机器人账号给这个人设置的备注
-     */
-    @MiraiExperimentalAPI("还未支持")
-    actual abstract suspend fun queryRemark(): FriendNameRemark
-
     /**
      * 向这个对象发送消息.
      *
@@ -90,7 +68,17 @@ actual abstract class QQ : Contact(), CoroutineScope {
      * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
      */
     @JvmSynthetic
-    actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<QQ>
+    abstract override suspend fun sendMessage(message: Message): MessageReceipt<User>
+
+    /**
+     * @see sendMessage
+     */
+    @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE", "VIRTUAL_MEMBER_HIDDEN", "OVERRIDE_BY_INLINE")
+    @kotlin.internal.InlineOnly // purely virtual
+    @JvmSynthetic
+    suspend inline fun sendMessage(message: String): MessageReceipt<User> {
+        return sendMessage(message.toMessage())
+    }
 
     /**
      * 上传一个图片以备发送.
@@ -102,5 +90,7 @@ actual abstract class QQ : Contact(), CoroutineScope {
      * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
      */
     @JvmSynthetic
-    actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
+    abstract override suspend fun uploadImage(image: ExternalImage): OfflineFriendImage
+
+    abstract override fun toString(): String
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/javaFriendly.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/javaFriendly.kt
index b20b7f897..49bfeaf45 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/javaFriendly.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/javaFriendly.kt
@@ -13,15 +13,18 @@ import net.mamoe.mirai.JavaFriendlyAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
 
 /**
- * [Contact] 中为了让 `Java` 更容易调用的 API
+ * [Contact] 中为了让 `Java` 更容易调用的 API.
+ * 不要用它作为一个类型, 只应使用其中的方法
  */
 @MiraiInternalAPI
 @JavaFriendlyAPI
-expect abstract class ContactJavaFriendlyAPI()
+expect abstract class ContactJavaFriendlyAPI internal constructor()
 
 /**
  * [Member] 中为了让 `Java` 更容易调用的 API
+ * 不要用它作为一个类型, 只应使用其中的方法
  */
+@Suppress("DEPRECATION_ERROR")
 @MiraiInternalAPI
 @JavaFriendlyAPI
-expect abstract class MemberJavaFriendlyAPI : QQ
\ No newline at end of file
+expect abstract class MemberJavaFriendlyAPI internal constructor() : QQ // 将来会改为 User
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt
index 9a7e750dd..560cf5c68 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt
@@ -241,7 +241,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
 
     /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
     @MessageDsl
-    fun sentBy(friend: QQ): ListeningFilter = content { sender.id == friend.id }
+    fun sentBy(friend: User): ListeningFilter = content { sender.id == friend.id }
 
     /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
     @MessageDsl
@@ -294,7 +294,7 @@ open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
 
     /** [消息内容][Message.contentToString]包含目标为 [target] 的 [At] */
     @MessageDsl
-    fun at(target: QQ): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target.id }
+    fun at(target: User): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == target.id }
 
     /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At], 就执行 [onEvent] */
     @MessageDsl
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
index 6d1587c61..1a2ae792f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
@@ -102,7 +102,7 @@ sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractCancellableEve
     ) : MessageSendEvent(), CancellableEvent
 
     data class FriendMessageSendEvent(
-        override val target: QQ,
+        override val target: Friend,
         var message: MessageChain
     ) : MessageSendEvent(), CancellableEvent
 }
@@ -539,9 +539,17 @@ data class MemberUnmuteEvent(
 @SinceMirai("0.36.0")
 data class FriendRemarkChangeEvent(
     override val bot: Bot,
-    val friend: QQ,
+    val friend: Friend,
     val newName: String
-) : BotEvent, Packet
+) : BotEvent, Packet {
+
+    @Deprecated("", level = DeprecationLevel.HIDDEN)
+    @get:JvmSynthetic
+    @get:JvmName("getFriend")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    val friendDeprecated: QQ
+        get() = friend
+}
 
 /**
  * 成功添加了一个新好友的事件
@@ -551,9 +559,16 @@ data class FriendAddEvent(
     /**
      * 新好友. 已经添加到 [Bot.friends]
      */
-    val friend: QQ
+    val friend: Friend
 ) : BotEvent, Packet {
     override val bot: Bot get() = friend.bot
+
+    @Deprecated("", level = DeprecationLevel.HIDDEN)
+    @get:JvmSynthetic
+    @get:JvmName("getFriend")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    val friendDeprecated: QQ
+        get() = friend
 }
 
 /**
@@ -561,9 +576,16 @@ data class FriendAddEvent(
  */
 @SinceMirai("0.36.0")
 data class FriendDeleteEvent(
-    val friend: QQ
+    val friend: Friend
 ) : BotEvent, Packet {
     override val bot: Bot get() = friend.bot
+
+    @Deprecated("", level = DeprecationLevel.HIDDEN)
+    @get:JvmSynthetic
+    @get:JvmName("getFriend")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    val friendDeprecated: QQ
+        get() = friend
 }
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/types.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/types.kt
index ac811712b..1019a6f1a 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/types.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/types.kt
@@ -10,11 +10,13 @@
 package net.mamoe.mirai.event.events
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
+import kotlin.jvm.JvmName
+import kotlin.jvm.JvmSynthetic
 
 /**
  * 有关一个 [Bot] 的事件
@@ -85,7 +87,14 @@ val GroupOperableEvent.operatorOrBot: Member
  * 有关好友的事件
  */
 interface FriendEvent : BotEvent {
-    val friend: QQ
+    val friend: Friend
     override val bot: Bot
         get() = friend.bot
+
+    @Deprecated("", level = DeprecationLevel.HIDDEN)
+    @get:JvmSynthetic
+    @get:JvmName("getFriend")
+    @Suppress("INAPPLICABLE_JVM_NAME", "DEPRECATION_ERROR")
+    val friendDeprecated: net.mamoe.mirai.contact.QQ
+        get() = friend
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
index daff84a10..b0b3daa5e 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/lowLevelApi.kt
@@ -10,8 +10,8 @@
 package net.mamoe.mirai
 
 import kotlinx.coroutines.Job
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.Group
-import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.data.*
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.SinceMirai
@@ -37,13 +37,13 @@ annotation class LowLevelAPI
 @LowLevelAPI
 interface LowLevelBotAPIAccessor {
     /**
-     * 构造一个 [_lowLevelNewQQ] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
+     * 构造一个 [Friend] 对象. 它持有对 [Bot] 的弱引用([WeakRef]).
      *
      * [Bot] 无法管理这个对象, 但这个对象会以 [Bot] 的 [Job] 作为父 Job.
      * 因此, 当 [Bot] 被关闭后, 这个对象也会被关闭.
      */
     @LowLevelAPI
-    fun _lowLevelNewQQ(friendInfo: FriendInfo): QQ
+    fun _lowLevelNewFriend(friendInfo: FriendInfo): Friend
 
     /**
      * 向服务器查询群列表. 返回值高 32 bits 为 uin, 低 32 bits 为 groupCode
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt
index ee84de497..df229c4d1 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/ContactMessage.kt
@@ -23,10 +23,7 @@ import kotlinx.coroutines.TimeoutCancellationException
 import kotlinx.coroutines.async
 import kotlinx.coroutines.io.ByteReadChannel
 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.contact.*
 import net.mamoe.mirai.event.events.BotEvent
 import net.mamoe.mirai.event.selectMessages
 import net.mamoe.mirai.event.syncFromEvent
@@ -52,7 +49,7 @@ import kotlin.jvm.JvmSynthetic
  */
 @Suppress("DEPRECATION")
 @SinceMirai("0.32.0")
-abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent
+abstract class ContactMessage : MessagePacket<User, Contact>(), BotEvent
 
 /**
  * 一条从服务器接收到的消息事件.
@@ -63,7 +60,7 @@ abstract class ContactMessage : MessagePacket<QQ, Contact>(), BotEvent
     message = "use ContactMessage",
     replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
 )
-expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructor() :
+expect abstract class MessagePacket<TSender : User, TSubject : Contact> constructor() :
     MessagePacketBase<TSender, TSubject>
 
 /**
@@ -74,7 +71,7 @@ expect abstract class MessagePacket<TSender : QQ, TSubject : Contact> constructo
     replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
 )
 @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
-abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Packet, BotEvent {
+abstract class MessagePacketBase<out TSender : User, out TSubject : Contact> : Packet, BotEvent {
     /**
      * 接受到这条消息的
      */
@@ -105,6 +102,10 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
      */
     abstract val message: MessageChain
 
+    /**
+     * 消息源
+     */
+    open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
 
     // region 发送 Message
 
@@ -118,18 +119,11 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
 
     suspend inline fun reply(plain: String): MessageReceipt<TSubject> =
         subject.sendMessage(plain.toMessage().asMessageChain()) as MessageReceipt<TSubject>
-
     // endregion
 
-    // region 撤回
-
-    // endregion
-
-    // region 上传图片
+    // region 图片
     suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
-    // endregion
 
-    // region 发送图片
     suspend inline fun ExternalImage.send(): MessageReceipt<TSubject> = this.sendTo(subject)
 
     suspend inline fun Image.send(): MessageReceipt<TSubject> = this.sendTo(subject)
@@ -159,22 +153,12 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
     @JvmName("reply2")
     suspend inline fun MessageChain.quoteReply(): MessageReceipt<TSubject> = quoteReply(this)
 
-    open val source: OnlineMessageSource.Incoming get() = message.source as OnlineMessageSource.Incoming
-
     // endregion
 
-    operator fun <M : Message> get(at: Message.Key<M>): M {
+    inline operator fun <M : Message> get(at: Message.Key<M>): M {
         return this.message[at]
     }
 
-    /**
-     * 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [IllegalArgumentException]
-     */
-    fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
-
-    fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target)
-        ?: error("`At.member` can only be used in GroupMessage")
-
     inline fun At.isBot(): Boolean = target == bot.id
 
     // endregion
@@ -202,13 +186,40 @@ abstract class MessagePacketBase<out TSender : QQ, out TSubject : Contact> : Pac
     // endregion
 
 
-    @Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"))
+    @PlannedRemoval("1.0.0")
+    @Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"),
+        DeprecationLevel.ERROR)
     @JvmName("reply1")
     suspend inline fun String.reply(): MessageReceipt<TSubject> = reply(this)
 
-    @Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"))
+    @PlannedRemoval("1.0.0")
+    @Deprecated("use reply(String) for clear semantics", ReplaceWith("reply(this)"),
+        level = DeprecationLevel.ERROR)
     @JvmName("reply1")
     suspend inline fun Message.reply(): MessageReceipt<TSubject> = reply(this)
+
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    @get:JvmSynthetic
+    @get:JvmName("getSender")
+    @Suppress("DEPRECATION_ERROR", "INAPPLICABLE_JVM_NAME")
+    val senderDeprecated: QQ
+        get() = sender as QQ
+
+    @Suppress("DEPRECATION_ERROR")
+    @PlannedRemoval("1.0.0")
+    @Deprecated("removed",
+        level = DeprecationLevel.ERROR,
+        replaceWith = ReplaceWith("At(this as? Member ?: error(\"`QQ.at` can only be used in GroupMessage\"))",
+            "net.mamoe.mirai.message.data.At",
+            "net.mamoe.mirai.contact.Member"))
+    fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("removed",
+        level = DeprecationLevel.ERROR,
+        replaceWith = ReplaceWith("(this@MessagePacketBase as? GroupMessage)?.group?.get(this.target) ?: error(\"`At.member` can only be used in GroupMessage\")"))
+    fun At.member(): Member = (this@MessagePacketBase as? GroupMessage)?.group?.get(this.target)
+        ?: error("`At.member` can only be used in GroupMessage")
 }
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
index e0075ddd5..8930500e0 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/FriendMessage.kt
@@ -7,15 +7,19 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
+@file:Suppress("DEPRECATION_ERROR")
+
 package net.mamoe.mirai.message
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.Friend
 import net.mamoe.mirai.contact.QQ
 import net.mamoe.mirai.event.BroadcastControllable
 import net.mamoe.mirai.message.data.MessageChain
 import net.mamoe.mirai.message.data.MessageSource
 import net.mamoe.mirai.message.data.OnlineMessageSource
 import net.mamoe.mirai.message.data.source
+import net.mamoe.mirai.utils.PlannedRemoval
 import net.mamoe.mirai.utils.getValue
 import net.mamoe.mirai.utils.unsafeWeakRef
 
@@ -23,17 +27,21 @@ import net.mamoe.mirai.utils.unsafeWeakRef
  * 好友消息
  */
 class FriendMessage(
-    sender: QQ,
+    sender: Friend,
     override val message: MessageChain
 ) : ContactMessage(), BroadcastControllable {
+    @PlannedRemoval("1.0.0")
+    @Deprecated("", level = DeprecationLevel.HIDDEN)
+    constructor(sender: QQ, message: MessageChain) : this(sender as Friend, message)
+
     init {
         val source = message.getOrNull(MessageSource) ?: error("Cannot find MessageSource from message")
         check(source is OnlineMessageSource.Incoming.FromFriend) { "source provided to a FriendMessage must be an instance of OnlineMessageSource.Incoming.FromFriend" }
     }
 
-    override val sender: QQ by sender.unsafeWeakRef()
+    override val sender: Friend by sender.unsafeWeakRef()
     override val bot: Bot get() = sender.bot
-    override val subject: QQ get() = sender
+    override val subject: Friend get() = sender
     override val source: OnlineMessageSource.Incoming.FromFriend get() = message.source as OnlineMessageSource.Incoming.FromFriend
 
     override fun toString(): String = "FriendMessage(sender=${sender.id}, message=$message)"
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 fc961d633..c457c9517 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
@@ -14,10 +14,7 @@ import net.mamoe.mirai.contact.Group
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.MemberPermission
 import net.mamoe.mirai.event.Event
-import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.message.data.MessageSource
-import net.mamoe.mirai.message.data.OnlineMessageSource
-import net.mamoe.mirai.message.data.source
+import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.getValue
 import net.mamoe.mirai.utils.unsafeWeakRef
 
@@ -44,7 +41,7 @@ class GroupMessage(
 
     override val source: OnlineMessageSource.Incoming.FromGroup get() = message.source as OnlineMessageSource.Incoming.FromGroup
 
-    inline fun Long.member(): Member = group[this]
+    inline fun At.asMember(): Member = group[this.target]
 
     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/message/data/MessageSource.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
index 291ca89c6..0e7806620 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt
@@ -116,14 +116,12 @@ sealed class OnlineMessageSource : MessageSource() {
      * 消息发送人. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群员][Member].
      * 即类型必定为 [Bot], [QQ] 或 [Member]
      */
-    @ExperimentalIdentification
     abstract val sender: ContactOrBot
 
     /**
      * 消息发送目标. 可能为 [机器人][Bot] 或 [好友][QQ] 或 [群][Group].
      * 即类型必定为 [Bot], [QQ] 或 [Group]
      */
-    @ExperimentalIdentification
     abstract val target: ContactOrBot
 
     /**
@@ -135,7 +133,6 @@ sealed class OnlineMessageSource : MessageSource() {
     /**
      * 由 [机器人主动发送消息][Contact.sendMessage] 产生的 [MessageSource]
      */
-    @OptIn(ExperimentalIdentification::class)
     sealed class Outgoing : OnlineMessageSource() {
         companion object Key : Message.Key<Outgoing> {
             override val typeName: String get() = "OnlineMessageSource.Outgoing"
@@ -152,8 +149,8 @@ sealed class OnlineMessageSource : MessageSource() {
                 override val typeName: String get() = "OnlineMessageSource.Outgoing.ToFriend"
             }
 
-            abstract override val target: QQ
-            final override val subject: QQ get() = target
+            abstract override val target: Friend
+            final override val subject: Friend get() = target
             //  final override fun toString(): String = "OnlineMessageSource.ToFriend(target=${target.id})"
         }
 
@@ -181,13 +178,12 @@ sealed class OnlineMessageSource : MessageSource() {
     /**
      * 接收到的一条消息的 [MessageSource]
      */
-    @OptIn(ExperimentalIdentification::class)
     sealed class Incoming : OnlineMessageSource() {
         companion object Key : Message.Key<Incoming> {
             override val typeName: String get() = "OnlineMessageSource.Incoming"
         }
 
-        abstract override val sender: QQ // out QQ
+        abstract override val sender: User
 
         final override val fromId: Long get() = sender.id
         final override val targetId: Long get() = target.id
@@ -197,8 +193,8 @@ sealed class OnlineMessageSource : MessageSource() {
                 override val typeName: String get() = "OnlineMessageSource.Incoming.FromFriend"
             }
 
-            abstract override val sender: QQ
-            final override val subject: QQ get() = sender
+            abstract override val sender: Friend
+            final override val subject: Friend get() = sender
             final override val target: Bot get() = sender.bot
             // final override fun toString(): String = "OnlineMessageSource.FromFriend(from=${sender.id})"
         }
@@ -243,7 +239,6 @@ sealed class OnlineMessageSource : MessageSource() {
     @Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
     @get:JvmName("target")
     @get:JvmSynthetic
-    @OptIn(ExperimentalIdentification::class)
     open val target2: Any
         get() = target
 
@@ -251,7 +246,6 @@ sealed class OnlineMessageSource : MessageSource() {
     @Deprecated("for binary compatibility until 1.0.0", level = DeprecationLevel.HIDDEN)
     @get:JvmName("sender")
     @get:JvmSynthetic
-    @OptIn(ExperimentalIdentification::class)
     open val sender2: Any
         get() = sender
 }
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 d0dbdd67c..7ef989a4d 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
@@ -7,7 +7,7 @@
  * https://github.com/mamoe/mirai/blob/master/LICENSE
  */
 
-@file:Suppress("EXPERIMENTAL_API_USAGE")
+@file:Suppress("EXPERIMENTAL_API_USAGE", "unused")
 
 package net.mamoe.mirai.utils
 
@@ -18,7 +18,7 @@ import kotlinx.io.core.Input
 import kotlinx.serialization.InternalSerializationApi
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
-import net.mamoe.mirai.contact.QQ
+import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.message.MessageReceipt
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.OfflineImage
@@ -153,7 +153,7 @@ class ExternalImage private constructor(
 @JvmSynthetic
 suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> = when (contact) {
     is Group -> contact.uploadImage(this).sendTo(contact)
-    is QQ -> contact.uploadImage(this).sendTo(contact)
+    is User -> contact.uploadImage(this).sendTo(contact)
     else -> error("unreachable")
 }
 
@@ -166,7 +166,7 @@ suspend fun <C : Contact> ExternalImage.sendTo(contact: C): MessageReceipt<C> =
 @JvmSynthetic
 suspend fun ExternalImage.upload(contact: Contact): OfflineImage = when (contact) {
     is Group -> contact.uploadImage(this)
-    is QQ -> contact.uploadImage(this)
+    is User -> contact.uploadImage(this)
     else -> error("unreachable")
 }
 
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt
index 7c6f8b5db..ef3295b71 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/BotJavaFriendlyAPI.kt
@@ -1,7 +1,6 @@
 package net.mamoe.mirai
 
 import kotlinx.coroutines.*
-import net.mamoe.mirai.contact.PermissionDeniedException
 import net.mamoe.mirai.contact.recall
 import net.mamoe.mirai.data.AddFriendResult
 import net.mamoe.mirai.message.MessageReceipt
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaFriendlyAPI.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaFriendlyAPI.kt
index 8a79554dd..6e0b96bcf 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaFriendlyAPI.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/ContactJavaFriendlyAPI.kt
@@ -205,7 +205,7 @@ actual abstract class ContactJavaFriendlyAPI {
     }
 }
 
-@Suppress("INAPPLICABLE_JVM_NAME", "FunctionName", "unused", "unused")
+@Suppress("INAPPLICABLE_JVM_NAME", "FunctionName", "unused", "unused", "DEPRECATION_ERROR")
 @MiraiInternalAPI
 @JavaFriendlyAPI
 actual abstract class MemberJavaFriendlyAPI : QQ() {
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Group.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Group.kt
deleted file mode 100644
index 7f49e77f8..000000000
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Group.kt
+++ /dev/null
@@ -1,171 +0,0 @@
-/*
- * 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.contact
-
-import kotlinx.coroutines.CoroutineScope
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.LowLevelAPI
-import net.mamoe.mirai.data.MemberInfo
-import net.mamoe.mirai.event.events.*
-import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
-import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
-import net.mamoe.mirai.message.MessageReceipt
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.OfflineGroupImage
-import net.mamoe.mirai.utils.ExternalImage
-import net.mamoe.mirai.utils.MiraiExperimentalAPI
-import net.mamoe.mirai.utils.OverFileSizeMaxException
-import net.mamoe.mirai.utils.SinceMirai
-
-/**
- * 群. 在 QQ Android 中叫做 "Troop"
- */
-@Suppress("INAPPLICABLE_JVM_NAME")
-actual abstract class Group : Contact(), CoroutineScope {
-    /**
-     * 群名称.
-     *
-     * 在修改时将会异步上传至服务器.
-     * 频繁修改可能会被服务器拒绝.
-     *
-     * @see MemberPermissionChangeEvent
-     * @throws PermissionDeniedException 无权限修改时将会抛出异常
-     */
-    actual abstract var name: String
-
-    /**
-     * 群设置
-     */
-    @SinceMirai("0.30.0")
-    actual abstract val settings: GroupSettings
-
-    /**
-     * 同为 groupCode, 用户看到的群号码.
-     */
-    actual abstract override val id: Long
-
-    /**
-     * 群主.
-     *
-     * @return 若机器人是群主, 返回 [botAsMember]. 否则返回相应的成员
-     */
-    actual abstract val owner: Member
-
-    /**
-     * [Bot] 在群内的 [newMember] 实例
-     */
-    @MiraiExperimentalAPI
-    actual abstract val botAsMember: Member
-
-    /**
-     * 机器人被禁言还剩余多少秒
-     *
-     * @see BotMuteEvent 机器人被禁言事件
-     * @see isBotMuted 判断机器人是否正在被禁言
-     */
-    actual abstract val botMuteRemaining: Int
-
-    /**
-     * 机器人在这个群里的权限
-     *
-     * @see Group.checkBotPermission 检查 [Bot] 在这个群里的权限
-     * @see Group.checkBotPermissionOperator 要求 [Bot] 在这个群里的权限为 [管理员或群主][MemberPermission.isOperator]
-     *
-     * @see BotGroupPermissionChangeEvent 机器人群员修改
-     */
-    actual abstract val botPermission: MemberPermission
-
-    /**
-     * 群头像下载链接.
-     */
-    actual val avatarUrl: String
-        get() = "https://p.qlogo.cn/gh/$id/${id}_1/640"
-
-    /**
-     * 群成员列表, 不含机器人自己, 含群主.
-     * 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新
-     */
-    actual abstract val members: ContactList<Member>
-
-    /**
-     * 获取群成员实例. 不存在时抛出 [kotlin.NoSuchElementException]
-     * 当 [id] 为 [Bot.id] 时返回 [botAsMember]
-     */
-    actual abstract operator fun get(id: Long): Member
-
-    /**
-     * 获取群成员实例, 不存在则 null
-     * 当 [id] 为 [Bot.id] 时返回 [botAsMember]
-     */
-    actual abstract fun getOrNull(id: Long): Member?
-
-
-    /**
-     * 检查此 id 的群成员是否存在
-     * 当 [id] 为 [Bot.id] 时返回 `true`
-     */
-    actual abstract operator fun contains(id: Long): Boolean
-
-    /**
-     * 让机器人退出这个群. 机器人必须为非群主才能退出. 否则将会失败
-     */
-    @SinceMirai("0.37.0")
-    actual abstract suspend fun quit(): Boolean
-
-    /**
-     * 构造一个 [Member].
-     * 非特殊情况请不要使用这个函数. 优先使用 [get].
-     */
-    @LowLevelAPI
-    @MiraiExperimentalAPI("dangerous")
-    actual abstract fun newMember(memberInfo: MemberInfo): Member
-
-    /**
-     * 向这个对象发送消息.
-     *
-     * 单条消息最大可发送 4500 字符或 50 张图片.
-     *
-     * @see FriendMessageSendEvent 发送好友信息事件, cancellable
-     * @see GroupMessageSendEvent  发送群消息事件. cancellable
-     *
-     * @throws EventCancelledException 当发送消息事件被取消时抛出
-     * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
-     * @throws MessageTooLargeException 当消息过长时抛出
-     *
-     * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
-     */
-    @JvmSynthetic
-    actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<Group>
-
-    /**
-     * 上传一个图片以备发送.
-     *
-     * @see BeforeImageUploadEvent 图片上传前事件, cancellable
-     * @see ImageUploadEvent 图片上传完成事件
-     *
-     * @throws EventCancelledException 当发送消息事件被取消
-     * @throws OverFileSizeMaxException 当图片文件过大而被服务器拒绝上传时. (最大大小约为 20 MB)
-     */
-    @JvmSynthetic
-    actual abstract override suspend fun uploadImage(image: ExternalImage): OfflineGroupImage
-
-    actual companion object {
-        actual fun calculateGroupUinByGroupCode(groupCode: Long): Long =
-            CommonGroupCalculations.calculateGroupUinByGroupCode(groupCode)
-
-        actual fun calculateGroupCodeByGroupUin(groupUin: Long): Long =
-            CommonGroupCalculations.calculateGroupCodeByGroupUin(groupUin)
-    }
-
-    @MiraiExperimentalAPI
-    actual fun toFullString(): String {
-        return "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})"
-    }
-}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Member.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Member.kt
deleted file mode 100644
index 8afbf1231..000000000
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/Member.kt
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * 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.contact
-
-import net.mamoe.mirai.Bot
-import net.mamoe.mirai.JavaFriendlyAPI
-import net.mamoe.mirai.event.events.*
-import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
-import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
-import net.mamoe.mirai.message.MessageReceipt
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.utils.MiraiInternalAPI
-import net.mamoe.mirai.utils.WeakRefProperty
-
-/**
- * 群成员.
- */
-@OptIn(MiraiInternalAPI::class, JavaFriendlyAPI::class)
-@Suppress("INAPPLICABLE_JVM_NAME")
-actual abstract class Member : MemberJavaFriendlyAPI() {
-    /**
-     * 所在的群.
-     */
-    @WeakRefProperty
-    actual abstract val group: Group
-
-    /**
-     * 成员的权限, 动态更新.
-     *
-     * @see MemberPermissionChangeEvent 权限变更事件. 由群主或机器人的操作触发.
-     */
-    actual abstract val permission: MemberPermission
-    /**
-     * 群名片. 可能为空.
-     *
-     * 管理员和群主都可修改任何人(包括群主)的群名片.
-     *
-     * 在修改时将会异步上传至服务器.
-     *
-     * @see [nameCardOrNick] 获取非空群名片或昵称
-     *
-     * @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
-     * @throws PermissionDeniedException 无权限修改时
-     */
-    actual abstract var nameCard: String
-    /**
-     * 群头衔.
-     *
-     * 仅群主可以修改群头衔.
-     *
-     * 在修改时将会异步上传至服务器.
-     *
-     * @see MemberSpecialTitleChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件. 修改时也会触发此事件.
-     * @throws PermissionDeniedException 无权限修改时
-     */
-    actual abstract var specialTitle: String
-    /**
-     * 被禁言剩余时长. 单位为秒.
-     *
-     * @see isMuted 判断改成员是否处于禁言状态
-     * @see mute 设置禁言
-     * @see unmute 取消禁言
-     */
-    actual abstract val muteTimeRemaining: Int
-
-    /**
-     * 禁言.
-     *
-     * QQ 中最小操作和显示的时间都是一分钟.
-     * 机器人可以实现精确到秒, 会被客户端显示为 1 分钟但不影响实际禁言时间.
-     *
-     * 管理员可禁言成员, 群主可禁言管理员和群员.
-     *
-     * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
-     * @return 机器人无权限时返回 `false`
-     *
-     * @see Int.minutesToSeconds
-     * @see Int.hoursToSeconds
-     * @see Int.daysToSeconds
-     *
-     * @see MemberMuteEvent 成员被禁言事件
-     * @throws PermissionDeniedException 无权限修改时
-     */
-    @JvmSynthetic
-    actual abstract suspend fun mute(durationSeconds: Int)
-
-    /**
-     * 解除禁言.
-     *
-     * 管理员可解除成员的禁言, 群主可解除管理员和群员的禁言.
-     *
-     * @see MemberUnmuteEvent 成员被取消禁言事件.
-     * @throws PermissionDeniedException 无权限修改时
-     */
-    @JvmSynthetic
-    actual abstract suspend fun unmute()
-
-    /**
-     * 踢出该成员.
-     *
-     * 管理员可踢出成员, 群主可踢出管理员和群员.
-     *
-     * @see MemberLeaveEvent.Kick 成员被踢出事件.
-     * @throws PermissionDeniedException 无权限修改时
-     */
-    @JvmSynthetic
-    actual abstract suspend fun kick(message: String)
-
-    /**
-     * 向这个对象发送消息.
-     *
-     * 单条消息最大可发送 4500 字符或 50 张图片.
-     *
-     * @see FriendMessageSendEvent 发送好友信息事件, cancellable
-     * @see GroupMessageSendEvent  发送群消息事件. cancellable
-     *
-     * @throws EventCancelledException 当发送消息事件被取消时抛出
-     * @throws BotIsBeingMutedException 发送群消息时若 [Bot] 被禁言抛出
-     * @throws MessageTooLargeException 当消息过长时抛出
-     *
-     * @return 消息回执. 可进行撤回 ([MessageReceipt.recall])
-     */
-    @JvmSynthetic
-    actual abstract override suspend fun sendMessage(message: Message): MessageReceipt<Member>
-
-    /**
-     * 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
-     */
-    actual abstract override fun equals(other: Any?): Boolean
-
-    /**
-     * @return `bot.hashCode() * 31 + id.hashCode()`
-     */
-    actual abstract override fun hashCode(): Int
-
-}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/PermissionDeniedException.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/PermissionDeniedException.kt
deleted file mode 100644
index 01ff0cef3..000000000
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/contact/PermissionDeniedException.kt
+++ /dev/null
@@ -1,9 +0,0 @@
-package net.mamoe.mirai.contact
-
-/**
- * 权限不足
- */ // 不要删除多平台结构
-actual class PermissionDeniedException : IllegalStateException {
-    actual constructor() : super("Permission denied")
-    actual constructor(message: String?) : super(message)
-}
\ No newline at end of file
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
index 249cec608..ff0df3075 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
@@ -16,7 +16,8 @@ import kotlinx.io.core.Input
 import kotlinx.io.core.Output
 import kotlinx.io.core.use
 import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.contact.QQ
+import net.mamoe.mirai.contact.Friend
+import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 import net.mamoe.mirai.utils.MiraiInternalAPI
@@ -38,7 +39,7 @@ import java.net.URL
     replaceWith = ReplaceWith("ContactMessage", "net.mamoe.mirai.message.ContactMessage")
 )
 @OptIn(MiraiInternalAPI::class, MiraiExperimentalAPI::class)
-actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor() :
+actual abstract class MessagePacket<TSender : User, TSubject : Contact> actual constructor() :
     MessagePacketBase<TSender, TSubject>() {
     // region 上传图片
     @JvmSynthetic