From 56cbe2d8a2a5edfe97951a469c040022dc8527eb Mon Sep 17 00:00:00 2001
From: Him188 <Him188@mamoe.net>
Date: Sun, 27 Jun 2021 17:26:09 +0800
Subject: [PATCH] Redesign notice handling and introduce
 `NoticeProcessorPipeline` part 2

Do not broadcast StrangerAddedEvent if added twice, fix stranger scope not closed

Do not add new instance if there is already one

Close and remove corresponding stranger instance if there is new friend.
---
 .../commonMain/kotlin/event/events/friend.kt  |  30 +--
 .../kotlin/event/events/stranger.kt           |  13 +-
 .../src/commonMain/kotlin/AbstractBot.kt      |   5 +-
 mirai-core/src/commonMain/kotlin/MiraiImpl.kt |   4 +
 .../src/commonMain/kotlin/QQAndroidBot.kt     |   6 -
 .../commonMain/kotlin/contact/AbstractUser.kt |  36 ++-
 .../commonMain/kotlin/contact/FriendImpl.kt   |  15 +-
 .../commonMain/kotlin/contact/GroupImpl.kt    |   3 +-
 .../kotlin/contact/NormalMemberImpl.kt        |  25 +-
 .../commonMain/kotlin/contact/StrangerImpl.kt |  16 +-
 .../kotlin/contact/info/FriendInfoImpl.kt     |   6 +-
 .../kotlin/contact/info/StrangerInfoImpl.kt   |   6 +-
 .../kotlin/network/QQAndroidClient.kt         |  12 +-
 .../network/components/ContactUpdater.kt      |  16 +-
 .../components/NoticeProcessorPipeline.kt     |  47 ++--
 .../network/notice/BinaryMessageProcessor.kt  |  68 +-----
 .../network/notice/FriendNoticeProcessor.kt   | 206 ++++++++++++++++
 .../notice/GroupEventProcessorContext.kt      |  66 ------
 .../notice/GroupListNoticeProcessor.kt        | 140 ++++++-----
 .../network/notice/GroupMessageProcessor.kt   |  23 +-
 .../network/notice/NewContactSupport.kt       | 113 +++++++++
 .../notice/OtherClientNoticeProcessor.kt      | 139 +++++++++++
 .../notice/PrivateMessageNoticeProcessor.kt   |  94 ++++++++
 .../network/notice/SystemMessageProcessor.kt  | 221 +-----------------
 .../network/notice/decoders/MsgInfoDecoder.kt |   2 +-
 .../notice/decoders/MsgType0x210Decoder.kt    |   2 +-
 .../protocol/data/jce/ReqPushStatus.kt        |   4 +-
 .../protocol/data/proto/msgType0x210.kt       |   2 +-
 .../network/protocol/packet/PacketFactory.kt  |  14 ++
 .../chat/receive/MessageSvc.PbSendMsg.kt      |   4 +-
 .../receive/MessageSvc.RequestPushStatus.kt   |  74 +-----
 .../packet/chat/receive/OnlinePush.ReqPush.kt | 170 ++------------
 .../commonMain/kotlin/utils/AtomicIntSeq.kt   |  78 +++++++
 33 files changed, 918 insertions(+), 742 deletions(-)
 create mode 100644 mirai-core/src/commonMain/kotlin/network/notice/FriendNoticeProcessor.kt
 delete mode 100644 mirai-core/src/commonMain/kotlin/network/notice/GroupEventProcessorContext.kt
 create mode 100644 mirai-core/src/commonMain/kotlin/network/notice/NewContactSupport.kt
 create mode 100644 mirai-core/src/commonMain/kotlin/network/notice/OtherClientNoticeProcessor.kt
 create mode 100644 mirai-core/src/commonMain/kotlin/network/notice/PrivateMessageNoticeProcessor.kt
 create mode 100644 mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt

diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt b/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt
index 5a38dd007..d28495c0c 100644
--- a/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt
+++ b/mirai-core-api/src/commonMain/kotlin/event/events/friend.kt
@@ -28,34 +28,34 @@ import java.util.concurrent.atomic.AtomicBoolean
 /**
  * 好友昵称改变事件. 目前仅支持解析 (来自 PC 端的修改).
  */
-public data class FriendRemarkChangeEvent internal constructor(
+public data class FriendRemarkChangeEvent @MiraiInternalApi public constructor(
     public override val friend: Friend,
     public val oldRemark: String,
-    public val newRemark: String
+    public val newRemark: String,
 ) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
 
 /**
  * 成功添加了一个新好友的事件
  */
-public data class FriendAddEvent @MiraiInternalApi constructor(
+public data class FriendAddEvent @MiraiInternalApi public constructor(
     /**
      * 新好友. 已经添加到 [Bot.friends]
      */
-    public override val friend: Friend
+    public override val friend: Friend,
 ) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
 
 /**
  * 好友已被删除或主动删除的事件.
  */
-public data class FriendDeleteEvent internal constructor(
-    public override val friend: Friend
+public data class FriendDeleteEvent @MiraiInternalApi public constructor(
+    public override val friend: Friend,
 ) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
 
 /**
  * 一个账号请求添加机器人为好友的事件
  */
 @Suppress("DEPRECATION")
-public data class NewFriendRequestEvent internal constructor(
+public data class NewFriendRequestEvent @MiraiInternalApi public constructor(
     public override val bot: Bot,
     /**
      * 事件唯一识别号
@@ -76,7 +76,7 @@ public data class NewFriendRequestEvent internal constructor(
     /**
      * 群名片或好友昵称
      */
-    public val fromNick: String
+    public val fromNick: String,
 ) : BotEvent, Packet, AbstractEvent(), FriendInfoChangeEvent {
     @JvmField
     internal val responded: AtomicBoolean = AtomicBoolean(false)
@@ -97,25 +97,25 @@ public data class NewFriendRequestEvent internal constructor(
 /**
  * [Friend] 头像被修改. 在此事件广播前就已经修改完毕.
  */
-public data class FriendAvatarChangedEvent internal constructor(
-    public override val friend: Friend
+public data class FriendAvatarChangedEvent @MiraiInternalApi public constructor(
+    public override val friend: Friend,
 ) : FriendEvent, Packet, AbstractEvent()
 
 /**
  * [Friend] 昵称改变事件, 在此事件广播时好友已经完成改名
  * @see BotNickChangedEvent
  */
-public data class FriendNickChangedEvent internal constructor(
+public data class FriendNickChangedEvent @MiraiInternalApi public constructor(
     public override val friend: Friend,
     public val from: String,
-    public val to: String
+    public val to: String,
 ) : FriendEvent, Packet, AbstractEvent(), FriendInfoChangeEvent
 
 /**
  * 好友输入状态改变的事件,当开始输入文字、退出聊天窗口或清空输入框时会触发此事件
  */
-public data class FriendInputStatusChangedEvent internal constructor(
+public data class FriendInputStatusChangedEvent @MiraiInternalApi public constructor(
     public override val friend: Friend,
-    public val inputting: Boolean
+    public val inputting: Boolean,
 
-) : FriendEvent, Packet, AbstractEvent()
\ No newline at end of file
+    ) : FriendEvent, Packet, AbstractEvent()
\ No newline at end of file
diff --git a/mirai-core-api/src/commonMain/kotlin/event/events/stranger.kt b/mirai-core-api/src/commonMain/kotlin/event/events/stranger.kt
index 4d99b4336..d7e24b36d 100644
--- a/mirai-core-api/src/commonMain/kotlin/event/events/stranger.kt
+++ b/mirai-core-api/src/commonMain/kotlin/event/events/stranger.kt
@@ -24,7 +24,7 @@ public data class StrangerAddEvent @MiraiInternalApi public constructor(
     /**
      * 新的陌生人. 已经添加到 [Bot.strangers]
      */
-    public override val stranger: Stranger
+    public override val stranger: Stranger,
 ) : StrangerEvent, Packet, AbstractEvent()
 
 
@@ -33,19 +33,16 @@ public data class StrangerAddEvent @MiraiInternalApi public constructor(
  *
  */
 public sealed class StrangerRelationChangeEvent(
-    public override val stranger: Stranger
+    public override val stranger: Stranger,
 ) : StrangerEvent, Packet, AbstractEvent() {
     /**
-     * 主动删除陌生人或陌生人被删除的事件
-     *
-     * 除主动删除外,此事件为惰性广播,无法确保实时性
-     * 目前被动删除仅会在陌生人二次添加时才会进行广播
+     * 主动删除陌生人或陌生人被删除的事件, 不一定能接收到被动删除的事件
      */
     public class Deleted(
         /**
          * 被删除的陌生人
          */
-        stranger: Stranger
+        stranger: Stranger,
     ) : StrangerRelationChangeEvent(stranger)
 
     /**
@@ -63,7 +60,7 @@ public sealed class StrangerRelationChangeEvent(
          *
          * 已经添加到Bot的好友列表中
          */
-        public val friend: Friend
+        public val friend: Friend,
     ) : StrangerRelationChangeEvent(stranger)
 
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/AbstractBot.kt b/mirai-core/src/commonMain/kotlin/AbstractBot.kt
index d3a8246ab..59f504a43 100644
--- a/mirai-core/src/commonMain/kotlin/AbstractBot.kt
+++ b/mirai-core/src/commonMain/kotlin/AbstractBot.kt
@@ -95,10 +95,13 @@ internal abstract class AbstractBot constructor(
     final override val groups: ContactList<GroupImpl> = ContactList()
     final override val strangers: ContactList<StrangerImpl> = ContactList()
 
-    final override val asFriend: FriendImpl by lazy { Mirai.newFriend(this, FriendInfoImpl(uin, nick, "")).cast() }
+    final override val asFriend: FriendImpl by lazy {
+        Mirai.newFriend(this, FriendInfoImpl(uin, "", "")).cast()
+    } // nick is initialized later on login
     final override val asStranger: StrangerImpl by lazy {
         Mirai.newStranger(this, StrangerInfoImpl(bot.id, bot.nick)).cast()
     }
+    final override var nick: String by asFriend.info::nick
 
     override fun close(cause: Throwable?) {
         if (!this.isActive) return
diff --git a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt
index 4406f7511..27b76e3b8 100644
--- a/mirai-core/src/commonMain/kotlin/MiraiImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/MiraiImpl.kt
@@ -14,6 +14,8 @@ import io.ktor.client.engine.okhttp.*
 import io.ktor.client.features.*
 import io.ktor.client.request.*
 import io.ktor.client.request.forms.*
+import io.ktor.http.*
+import io.ktor.utils.io.core.*
 import kotlinx.coroutines.SupervisorJob
 import kotlinx.coroutines.currentCoroutineContext
 import kotlinx.io.core.discardExact
@@ -30,7 +32,9 @@ import net.mamoe.mirai.event.broadcast
 import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.internal.contact.*
 import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
+import net.mamoe.mirai.internal.contact.info.FriendInfoImpl.Companion.impl
 import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
+import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl.Companion.impl
 import net.mamoe.mirai.internal.message.*
 import net.mamoe.mirai.internal.message.DeepMessageRefiner.refineDeep
 import net.mamoe.mirai.internal.network.components.EventDispatcher
diff --git a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
index 5006134c1..30a5919c9 100644
--- a/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
+++ b/mirai-core/src/commonMain/kotlin/QQAndroidBot.kt
@@ -230,12 +230,6 @@ internal open class QQAndroidBot constructor(
         get() = client.wLoginSigInfo.sKey.data
             .fold(5381) { acc: Int, b: Byte -> acc + acc.shl(5) + b.toInt() }
             .and(Int.MAX_VALUE)
-
-    ///////////////////////////////////////////////////////////////////////////
-    // contacts
-    ///////////////////////////////////////////////////////////////////////////
-
-    override lateinit var nick: String
 }
 
 internal fun QQAndroidBot.getGroupByUinOrFail(uin: Long) =
diff --git a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
index 41874e0fe..c731dadfd 100644
--- a/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/AbstractUser.kt
@@ -10,6 +10,9 @@
 package net.mamoe.mirai.internal.contact
 
 import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.contact.Friend
+import net.mamoe.mirai.contact.Member
+import net.mamoe.mirai.contact.Stranger
 import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.data.UserInfo
 import net.mamoe.mirai.event.broadcast
@@ -28,17 +31,32 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.Cmd0x352
 import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
 import net.mamoe.mirai.internal.network.protocol.packet.chat.image.LongConn
 import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
+import net.mamoe.mirai.internal.utils.AtomicIntSeq
+import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
 import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.message.MessageReceipt
-import net.mamoe.mirai.message.data.Image
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.MessageChain
-import net.mamoe.mirai.message.data.isContentEmpty
+import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.*
+import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 
 internal val User.info: UserInfo? get() = this.castOrNull<AbstractUser>()?.info
 
+@Suppress("NOTHING_TO_INLINE")
+internal inline fun User.impl(): AbstractUser {
+    contract { returns() implies (this@impl is AbstractUser) }
+    check(this is AbstractUser)
+    return this
+}
+
+internal val User.correspondingMessageSourceKind
+    get() = when (this) {
+        is Friend -> MessageSourceKind.FRIEND
+        is Member -> MessageSourceKind.TEMP
+        is Stranger -> MessageSourceKind.STRANGER
+        else -> error("Unknown user: ${this::class.qualifiedName}")
+    }
+
 internal abstract class AbstractUser(
     bot: QQAndroidBot,
     parentCoroutineContext: CoroutineContext,
@@ -49,6 +67,9 @@ internal abstract class AbstractUser(
     final override var nick: String = userInfo.nick
     final override val remark: String = userInfo.remark
 
+    val messageSeq = AtomicIntSeq.forMessageSeq()
+    val fragmentedMessageMerger = C2CPkgMsgParsingCache()
+
     open val info: UserInfo = userInfo
 
     @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@@ -58,7 +79,8 @@ internal abstract class AbstractUser(
         }
         val resp = bot.network.run {
             LongConn.OffPicUp(
-                bot.client, Cmd0x352.TryUpImgReq(
+                bot.client,
+                Cmd0x352.TryUpImgReq(
                     buType = 1,
                     srcUin = bot.id,
                     dstUin = this@AbstractUser.id,
@@ -66,8 +88,8 @@ internal abstract class AbstractUser(
                     fileSize = resource.size,
                     fileName = resource.md5.toUHexString("") + "." + resource.formatName,
                     imgOriginal = true,
-                    buildVer = bot.client.buildVer
-                )
+                    buildVer = bot.client.buildVer,
+                ),
             ).sendAndExpect<LongConn.OffPicUp.Response>()
         }
 
diff --git a/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt b/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt
index 35cf93d25..d06fe3d29 100644
--- a/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/FriendImpl.kt
@@ -14,12 +14,9 @@
 
 package net.mamoe.mirai.internal.contact
 
-import kotlinx.atomicfu.AtomicInt
-import kotlinx.atomicfu.atomic
 import net.mamoe.mirai.LowLevelApi
 import net.mamoe.mirai.Mirai
 import net.mamoe.mirai.contact.Friend
-import net.mamoe.mirai.data.FriendInfo
 import net.mamoe.mirai.event.events.FriendMessagePostSendEvent
 import net.mamoe.mirai.event.events.FriendMessagePreSendEvent
 import net.mamoe.mirai.internal.QQAndroidBot
@@ -31,7 +28,6 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.ImMsgBody
 import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.PttStore
 import net.mamoe.mirai.internal.network.protocol.packet.chat.voice.audioCodec
 import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
-import net.mamoe.mirai.internal.utils.C2CPkgMsgParsingCache
 import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.internal.utils.io.serialization.toByteArray
 import net.mamoe.mirai.message.MessageReceipt
@@ -53,9 +49,9 @@ internal fun net.mamoe.mirai.internal.network.protocol.data.jce.FriendInfo.toMir
     )
 
 @OptIn(ExperimentalContracts::class)
-internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
+internal inline fun Friend.impl(): FriendImpl {
     contract {
-        returns() implies (this@checkIsFriendImpl is FriendImpl)
+        returns() implies (this@impl is FriendImpl)
     }
     check(this is FriendImpl) { "A Friend instance is not instance of FriendImpl. Your instance: ${this::class.qualifiedName}" }
     return this
@@ -64,11 +60,8 @@ internal inline fun Friend.checkIsFriendImpl(): FriendImpl {
 internal class FriendImpl(
     bot: QQAndroidBot,
     parentCoroutineContext: CoroutineContext,
-    internal val friendInfo: FriendInfo,
-) : Friend, AbstractUser(bot, parentCoroutineContext, friendInfo) {
-    @Suppress("unused") // bug
-    val lastMessageSequence: AtomicInt = atomic(-1)
-    val friendPkgMsgParsingCache = C2CPkgMsgParsingCache()
+    override val info: FriendInfoImpl,
+) : Friend, AbstractUser(bot, parentCoroutineContext, info) {
     override suspend fun delete() {
         check(bot.friends[this.id] != null) {
             "Friend ${this.id} had already been deleted"
diff --git a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
index a297aadf6..c44bf909f 100644
--- a/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/GroupImpl.kt
@@ -303,7 +303,8 @@ internal fun Group.newMember(memberInfo: MemberInfo): Member {
     )
 }
 
-internal fun Group.addNewNormalMember(memberInfo: MemberInfo): NormalMemberImpl {
+internal fun Group.addNewNormalMember(memberInfo: MemberInfo): NormalMemberImpl? {
+    if (members.contains(memberInfo.uin)) return null
     return newNormalMember(memberInfo).also {
         members.delegate.add(it)
     }
diff --git a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
index bfab6397f..1eedb8e25 100644
--- a/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/NormalMemberImpl.kt
@@ -11,8 +11,6 @@
 
 package net.mamoe.mirai.internal.contact
 
-import kotlinx.atomicfu.AtomicInt
-import kotlinx.atomicfu.atomic
 import kotlinx.coroutines.CancellationException
 import kotlinx.coroutines.cancel
 import kotlinx.coroutines.launch
@@ -36,12 +34,9 @@ import kotlin.coroutines.CoroutineContext
 @Suppress("MemberVisibilityCanBePrivate")
 internal class NormalMemberImpl constructor(
     group: GroupImpl,
-    coroutineContext: CoroutineContext,
+    parentCoroutineContext: CoroutineContext,
     memberInfo: MemberInfo,
-) : NormalMember, AbstractMember(group, coroutineContext, memberInfo) {
-
-    @Suppress("unused") // false positive
-    val lastMessageSequence: AtomicInt = atomic(-1)
+) : NormalMember, AbstractMember(group, parentCoroutineContext, memberInfo) {
 
     override val joinTimestamp: Int get() = info.joinTimestamp
     override val lastSpeakTimestamp: Int get() = info.lastSpeakTimestamp
@@ -57,7 +52,7 @@ internal class NormalMemberImpl constructor(
             ?: handler.sendMessageImpl<NormalMember>(
                 message = message,
                 preSendEventConstructor = ::GroupTempMessagePreSendEvent,
-                postSendEventConstructor = ::GroupTempMessagePostSendEvent.cast()
+                postSendEventConstructor = ::GroupTempMessagePostSendEvent.cast(),
             )
     }
 
@@ -102,7 +97,7 @@ internal class NormalMemberImpl constructor(
                         TroopManagement.EditGroupNametag(
                             bot.client,
                             this@NormalMemberImpl,
-                            newValue
+                            newValue,
                         ).sendWithoutExpect()
                     }
                     MemberCardChangeEvent(oldValue, newValue, this@NormalMemberImpl).broadcast()
@@ -122,7 +117,7 @@ internal class NormalMemberImpl constructor(
                         TroopManagement.EditSpecialTitle(
                             bot.client,
                             this@NormalMemberImpl,
-                            newValue
+                            newValue,
                         ).sendWithoutExpect()
                     }
                     MemberSpecialTitleChangeEvent(oldValue, newValue, this@NormalMemberImpl, null).broadcast()
@@ -143,7 +138,7 @@ internal class NormalMemberImpl constructor(
                 client = bot.client,
                 groupCode = group.id,
                 memberUin = this@NormalMemberImpl.id,
-                timeInSecond = durationSeconds
+                timeInSecond = durationSeconds,
             ).sendAndExpect<TroopManagement.Mute.Response>()
         }
 
@@ -159,7 +154,7 @@ internal class NormalMemberImpl constructor(
                 client = bot.client,
                 groupCode = group.id,
                 memberUin = this@NormalMemberImpl.id,
-                timeInSecond = 0
+                timeInSecond = 0,
             ).sendAndExpect<TroopManagement.Mute.Response>()
         }
 
@@ -210,7 +205,7 @@ internal class NormalMemberImpl constructor(
             val resp: TroopManagement.ModifyAdmin.Response = TroopManagement.ModifyAdmin(
                 client = bot.client,
                 member = this@NormalMemberImpl,
-                operation = operation
+                operation = operation,
             ).sendAndExpect()
 
             check(resp.success) {
@@ -227,7 +222,7 @@ internal class NormalMemberImpl constructor(
 internal fun Member.checkBotPermissionHighest(operationName: String) {
     check(group.botPermission == MemberPermission.OWNER) {
         throw PermissionDeniedException(
-            "`$operationName` operation requires the OWNER permission, while bot has ${group.botPermission}"
+            "`$operationName` operation requires the OWNER permission, while bot has ${group.botPermission}",
         )
     }
 }
@@ -236,7 +231,7 @@ internal fun Member.checkBotPermissionHigherThanThis(operationName: String) {
     check(group.botPermission > this.permission) {
         throw PermissionDeniedException(
             "`$operationName` operation requires a higher permission, while " +
-                    "${group.botPermission} < ${this.permission}"
+                    "${group.botPermission} < ${this.permission}",
         )
     }
 }
diff --git a/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt b/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
index 2b900bf58..e43fcd2e2 100644
--- a/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/StrangerImpl.kt
@@ -17,8 +17,6 @@
 
 package net.mamoe.mirai.internal.contact
 
-import kotlinx.atomicfu.AtomicInt
-import kotlinx.atomicfu.atomic
 import net.mamoe.mirai.LowLevelApi
 import net.mamoe.mirai.contact.Stranger
 import net.mamoe.mirai.contact.User
@@ -39,10 +37,8 @@ import kotlin.coroutines.CoroutineContext
 
 
 @OptIn(ExperimentalContracts::class)
-internal inline fun Stranger.checkIsImpl(): StrangerImpl {
-    contract {
-        returns() implies (this@checkIsImpl is StrangerImpl)
-    }
+internal inline fun Stranger.impl(): StrangerImpl {
+    contract { returns() implies (this@impl is StrangerImpl) }
     check(this is StrangerImpl) { "A Stranger instance is not instance of StrangerImpl. Your instance: ${this::class.qualifiedName}" }
     return this
 }
@@ -50,10 +46,8 @@ internal inline fun Stranger.checkIsImpl(): StrangerImpl {
 internal class StrangerImpl(
     bot: QQAndroidBot,
     parentCoroutineContext: CoroutineContext,
-    internal val strangerInfo: StrangerInfo,
-) : Stranger, AbstractUser(bot, parentCoroutineContext, strangerInfo) {
-    @Suppress("unused") // bug
-    val lastMessageSequence: AtomicInt = atomic(-1)
+    override val info: StrangerInfo,
+) : Stranger, AbstractUser(bot, parentCoroutineContext, info) {
     override suspend fun delete() {
         check(bot.strangers[this.id] != null) {
             "Stranger ${this.id} had already been deleted"
@@ -66,7 +60,7 @@ internal class StrangerImpl(
         }
     }
 
-    private val handler by lazy { StrangerSendMessageHandler(this) }
+    private val handler: StrangerSendMessageHandler by lazy { StrangerSendMessageHandler(this) }
 
     @Suppress("DuplicatedCode")
     override suspend fun sendMessage(message: Message): MessageReceipt<Stranger> {
diff --git a/mirai-core/src/commonMain/kotlin/contact/info/FriendInfoImpl.kt b/mirai-core/src/commonMain/kotlin/contact/info/FriendInfoImpl.kt
index c46c8b135..bb1702798 100644
--- a/mirai-core/src/commonMain/kotlin/contact/info/FriendInfoImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/info/FriendInfoImpl.kt
@@ -18,4 +18,8 @@ internal data class FriendInfoImpl(
     override val uin: Long,
     override var nick: String,
     override var remark: String,
-) : FriendInfo
\ No newline at end of file
+) : FriendInfo {
+    companion object {
+        fun FriendInfo.impl() = if (this is FriendInfoImpl) this else FriendInfoImpl(uin, nick, remark)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/contact/info/StrangerInfoImpl.kt b/mirai-core/src/commonMain/kotlin/contact/info/StrangerInfoImpl.kt
index 6d55d00e1..6ec1a4604 100644
--- a/mirai-core/src/commonMain/kotlin/contact/info/StrangerInfoImpl.kt
+++ b/mirai-core/src/commonMain/kotlin/contact/info/StrangerInfoImpl.kt
@@ -20,4 +20,8 @@ internal class StrangerInfoImpl(
     override val nick: String,
     override val fromGroup: Long = 0,
     override val remark: String = "",
-) : StrangerInfo
\ No newline at end of file
+) : StrangerInfo {
+    companion object {
+        fun StrangerInfo.impl() = if (this is StrangerInfoImpl) this else StrangerInfoImpl(uin, nick, fromGroup, remark)
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt
index 574eea322..d79778e9b 100644
--- a/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt
+++ b/mirai-core/src/commonMain/kotlin/network/QQAndroidClient.kt
@@ -24,6 +24,7 @@ import net.mamoe.mirai.internal.network.components.SsoSession
 import net.mamoe.mirai.internal.network.protocol.SyncingCacheList
 import net.mamoe.mirai.internal.network.protocol.data.jce.FileStoragePushFSSvcList
 import net.mamoe.mirai.internal.network.protocol.packet.Tlv
+import net.mamoe.mirai.internal.utils.AtomicIntSeq
 import net.mamoe.mirai.internal.utils.MiraiProtocolInternal
 import net.mamoe.mirai.internal.utils.NetworkType
 import net.mamoe.mirai.utils.*
@@ -124,13 +125,10 @@ internal open class QQAndroidClient(
 
     internal var strangerSeq: Int = 0
 
-    // TODO: 2021/4/14 investigate whether they can be minimized
-    private val friendSeq: AtomicInt = atomic(getRandomUnsignedInt())
-    internal fun getFriendSeq(): Int = friendSeq.value
-
-    internal fun nextFriendSeq(): Int = friendSeq.incrementAndGet()
-
-    internal fun setFriendSeq(compare: Int, id: Int): Boolean = friendSeq.compareAndSet(compare, id % 65535)
+    /**
+     * for send
+     */
+    val sendFriendMessageSeq = AtomicIntSeq.forPrivateSync()
 
     internal val groupConfig: GroupConfig = GroupConfig()
 
diff --git a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt
index 093d90f29..0efb02ace 100644
--- a/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt
+++ b/mirai-core/src/commonMain/kotlin/network/components/ContactUpdater.kt
@@ -19,10 +19,8 @@ import kotlinx.coroutines.sync.withLock
 import kotlinx.coroutines.sync.withPermit
 import net.mamoe.mirai.Mirai
 import net.mamoe.mirai.contact.Contact
-import net.mamoe.mirai.data.FriendInfo
 import net.mamoe.mirai.data.MemberInfo
 import net.mamoe.mirai.internal.QQAndroidBot
-import net.mamoe.mirai.internal.contact.FriendImpl
 import net.mamoe.mirai.internal.contact.GroupImpl
 import net.mamoe.mirai.internal.contact.StrangerImpl
 import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
@@ -34,6 +32,7 @@ import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.component.ComponentKey
 import net.mamoe.mirai.internal.network.component.ComponentStorage
 import net.mamoe.mirai.internal.network.isValid
+import net.mamoe.mirai.internal.network.notice.NewContactSupport
 import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
 import net.mamoe.mirai.internal.network.protocol.data.jce.SvcRespRegister
 import net.mamoe.mirai.internal.network.protocol.data.jce.isValid
@@ -76,7 +75,7 @@ internal class ContactUpdaterImpl(
     val bot: QQAndroidBot, // not good
     val components: ComponentStorage,
     private val logger: MiraiLogger,
-) : ContactUpdater {
+) : ContactUpdater, NewContactSupport {
     override val otherClientsLock: Mutex = Mutex()
     override val groupListModifyLock: Mutex = Mutex()
     private val cacheService get() = components[ContactCacheService]
@@ -176,16 +175,13 @@ internal class ContactUpdaterImpl(
         }
 
         for (friendInfoImpl in list) {
-            addFriendToBot(friendInfoImpl)
+            bot.addNewFriendAndRemoveStranger(friendInfoImpl)
         }
 
 
         initFriendOk = true
     }
 
-    private fun addFriendToBot(it: FriendInfo) =
-        bot.friends.delegate.add(FriendImpl(bot, bot.coroutineContext, it))
-
     private suspend fun addGroupToBot(stTroopNum: StTroopNum) = stTroopNum.run {
         suspend fun refreshGroupMemberList(): Sequence<MemberInfo> {
             return Mirai.getRawGroupMemberList(
@@ -214,11 +210,11 @@ internal class ContactUpdaterImpl(
         bot.groups.delegate.add(
             GroupImpl(
                 bot = bot,
-                coroutineContext = bot.coroutineContext,
+                parentCoroutineContext = bot.coroutineContext,
                 id = groupCode,
                 groupInfo = GroupInfoImpl(stTroopNum),
-                members = members
-            )
+                members = members,
+            ),
         )
     }
 
diff --git a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt
index 0cee4b7ee..00ef4e189 100644
--- a/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt
+++ b/mirai-core/src/commonMain/kotlin/network/components/NoticeProcessorPipeline.kt
@@ -17,6 +17,7 @@ import net.mamoe.mirai.internal.network.component.ComponentStorage
 import net.mamoe.mirai.internal.network.notice.decoders.MsgType0x2DC
 import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo
 import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
+import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgOnlinePush
 import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMsgInfo
@@ -32,6 +33,19 @@ import kotlin.concurrent.read
 import kotlin.concurrent.write
 import kotlin.reflect.KClass
 
+/**
+ * Centralized processor pipeline for [MessageSvcPbGetMsg] and [OnlinePushPbPushTransMsg]
+ */
+internal interface NoticeProcessorPipeline {
+    fun registerProcessor(processor: NoticeProcessor)
+
+    suspend fun process(bot: QQAndroidBot, data: Any?, attributes: TypeSafeMap = TypeSafeMap()): Collection<Packet>
+
+    companion object : ComponentKey<NoticeProcessorPipeline> {
+        val ComponentStorage.noticeProcessorPipeline get() = get(NoticeProcessorPipeline)
+    }
+}
+
 internal interface PipelineContext {
     val bot: QQAndroidBot
     val attributes: TypeSafeMap
@@ -40,7 +54,7 @@ internal interface PipelineContext {
     val isConsumed: Boolean
 
     /**
-     * Mark the input as consumed so that there will not be warnings like 'Unknown type xxx'
+     * Mark the input as consumed so that there will not be warnings like 'Unknown type xxx'. This will not stop the pipeline.
      *
      * If this is executed, make sure you provided all information important for debugging.
      *
@@ -79,19 +93,6 @@ internal interface PipelineContext {
 
 internal inline val PipelineContext.context get() = this
 
-/**
- * Centralized processor pipeline for [MessageSvcPbGetMsg] and [OnlinePushPbPushTransMsg]
- */
-internal interface NoticeProcessorPipeline {
-    fun registerProcessor(processor: NoticeProcessor)
-
-    suspend fun process(bot: QQAndroidBot, data: Any?, attributes: TypeSafeMap = TypeSafeMap()): Collection<Packet>
-
-    companion object : ComponentKey<NoticeProcessorPipeline> {
-        val ComponentStorage.noticeProcessorPipeline get() = get(NoticeProcessorPipeline)
-    }
-}
-
 internal class NoticeProcessorPipelineImpl(
     private val logger: MiraiLogger,
 ) : NoticeProcessorPipeline {
@@ -142,6 +143,10 @@ internal class NoticeProcessorPipelineImpl(
 
 }
 
+///////////////////////////////////////////////////////////////////////////
+// NoticeProcessor
+///////////////////////////////////////////////////////////////////////////
+
 /**
  * A processor handling some specific type of message.
  */
@@ -157,11 +162,11 @@ internal abstract class SimpleNoticeProcessor<T : Any>(
 
     final override suspend fun process(context: PipelineContext, data: Any?) {
         if (type.isInstance(data)) {
-            context.process0(data.uncheckedCast())
+            context.processImpl(data.uncheckedCast())
         }
     }
 
-    protected abstract suspend fun PipelineContext.process0(data: T)
+    protected abstract suspend fun PipelineContext.processImpl(data: T)
 
     companion object {
         @JvmStatic
@@ -170,11 +175,11 @@ internal abstract class SimpleNoticeProcessor<T : Any>(
 }
 
 internal abstract class MsgCommonMsgProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
-    abstract override suspend fun PipelineContext.process0(data: MsgComm.Msg)
+    abstract override suspend fun PipelineContext.processImpl(data: MsgComm.Msg)
 }
 
 internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
-    final override suspend fun PipelineContext.process0(data: Any) {
+    final override suspend fun PipelineContext.processImpl(data: Any) {
         when (data) {
             is MsgInfo -> processImpl(data)
             is PbMsgInfo -> processImpl(data)
@@ -183,14 +188,16 @@ internal abstract class MixedNoticeProcessor : AnyNoticeProcessor() {
             is MsgType0x210 -> processImpl(data)
             is MsgType0x2DC -> processImpl(data)
             is Structmsg.StructMsg -> processImpl(data)
+            is RequestPushStatus -> processImpl(data)
         }
     }
 
     protected open suspend fun PipelineContext.processImpl(data: MsgInfo) {}
-    protected open suspend fun PipelineContext.processImpl(data: MsgType0x210) {}
-    protected open suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {}
+    protected open suspend fun PipelineContext.processImpl(data: MsgType0x210) {} // 528
+    protected open suspend fun PipelineContext.processImpl(data: MsgType0x2DC) {} // 732
     protected open suspend fun PipelineContext.processImpl(data: PbMsgInfo) {}
     protected open suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {}
     protected open suspend fun PipelineContext.processImpl(data: MsgComm.Msg) {}
     protected open suspend fun PipelineContext.processImpl(data: Structmsg.StructMsg) {}
+    protected open suspend fun PipelineContext.processImpl(data: RequestPushStatus) {}
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/BinaryMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/BinaryMessageProcessor.kt
index 4d80ec9a2..e056e2b18 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/BinaryMessageProcessor.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/BinaryMessageProcessor.kt
@@ -9,8 +9,6 @@
 
 package net.mamoe.mirai.internal.network.notice
 
-import kotlinx.io.core.discardExact
-import kotlinx.io.core.readUByte
 import net.mamoe.mirai.internal.message.contextualBugReportException
 import net.mamoe.mirai.internal.network.components.PipelineContext
 import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
@@ -18,27 +16,12 @@ import net.mamoe.mirai.internal.network.protocol.data.proto.OnlinePushTrans.PbMs
 import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.utils.read
 
-internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type()), GroupEventProcessorContext {
-    override suspend fun PipelineContext.process0(data: PbMsgInfo) {
+internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type()), NewContactSupport {
+    override suspend fun PipelineContext.processImpl(data: PbMsgInfo) {
         data.msgData.read<Unit> {
             when (data.msgType) {
                 44 -> {
-                    //                  3D C4 33 DD 01 FF CD 76 F4 03 C3 7E 2E 34
-                    //      群转让
-                    //      start with  3D C4 33 DD 01 FF
-                    //                  3D C4 33 DD 01 FF C3 7E 2E 34 CD 76 F4 03
-                    // 权限变更
-                    //                  3D C4 33 DD 01 00/01 .....
-                    //                  3D C4 33 DD 01 01 C3 7E 2E 34 01
-                    this.discardExact(5)
-                    when (val mode = readUByte().toInt()) {
-                        0xFF -> {
-                            TODO("removed")
-                        }
-                        else -> {
-                            TODO("removed")
-                        }
-                    }
+                    TODO("removed")
                 }
                 34 -> {
                     TODO("removed")
@@ -46,50 +29,7 @@ internal class BinaryMessageProcessor : SimpleNoticeProcessor<PbMsgInfo>(type())
                 else -> {
                     when {
                         data.msgType == 529 && data.msgSubtype == 9 -> {
-                            /*
-                            PbMsgInfo#1773430973 {
-fromUin=0x0000000026BA1173(649728371)
-generalFlag=0x00000001(1)
-msgData=0A 07 70 72 69 6E 74 65 72 10 02 1A CD 02 0A 1F 53 61 6D 73 75 6E 67 20 4D 4C 2D 31 38 36 30 20 53 65 72 69 65 73 20 28 55 53 42 30 30 31 29 0A 16 4F 6E 65 4E 6F 74 65 20 66 6F 72 20 57 69 6E 64 6F 77 73 20 31 30 0A 19 50 68 61 6E 74 6F 6D 20 50 72 69 6E 74 20 74 6F 20 45 76 65 72 6E 6F 74 65 0A 11 4F 6E 65 4E 6F 74 65 20 28 44 65 73 6B 74 6F 70 29 0A 1D 4D 69 63 72 6F 73 6F 66 74 20 58 50 53 20 44 6F 63 75 6D 65 6E 74 20 57 72 69 74 65 72 0A 16 4D 69 63 72 6F 73 6F 66 74 20 50 72 69 6E 74 20 74 6F 20 50 44 46 0A 15 46 6F 78 69 74 20 50 68 61 6E 74 6F 6D 20 50 72 69 6E 74 65 72 0A 03 46 61 78 32 09 0A 03 6A 70 67 10 01 18 00 32 0A 0A 04 6A 70 65 67 10 01 18 00 32 09 0A 03 70 6E 67 10 01 18 00 32 09 0A 03 67 69 66 10 01 18 00 32 09 0A 03 62 6D 70 10 01 18 00 32 09 0A 03 64 6F 63 10 01 18 01 32 0A 0A 04 64 6F 63 78 10 01 18 01 32 09 0A 03 74 78 74 10 00 18 00 32 09 0A 03 70 64 66 10 01 18 01 32 09 0A 03 70 70 74 10 01 18 01 32 0A 0A 04 70 70 74 78 10 01 18 01 32 09 0A 03 78 6C 73 10 01 18 01 32 0A 0A 04 78 6C 73 78 10 01 18 01
-msgSeq=0x00001AFF(6911)
-msgSubtype=0x00000009(9)
-msgTime=0x5FDF21A3(1608458659)
-msgType=0x00000211(529)
-msgUid=0x010000005FDEE04C(72057595646369868)
-realMsgTime=0x5FDF21A3(1608458659)
-svrIp=0x3E689409(1047041033)
-toUin=0x0000000026BA1173(649728371)
-}
-                             */
-                            /*
-                            *
-printer
-Samsung ML-1860 Series (USB001)
-OneNote for Windows 10
-Phantom Print to Evernote
-OneNote (Desktop)
-Microsoft XPS Document Writer
-Microsoft Print to PDF
-Foxit Phantom Printer
-Fax2
-jpg2
-
-jpeg2
-png2
-gif2
-bmp2
-doc2
-
-docx2
-txt2
-pdf2
-ppt2
-
-pptx2
-xls2
-
-xlsx*/
-                            return
+                            TODO("removed")
                         }
                     }
                     throw contextualBugReportException(
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/FriendNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/FriendNoticeProcessor.kt
new file mode 100644
index 000000000..4c2fbc65a
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/notice/FriendNoticeProcessor.kt
@@ -0,0 +1,206 @@
+/*
+ * Copyright 2019-2021 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.internal.network.notice
+
+import kotlinx.io.core.discardExact
+import kotlinx.io.core.readUByte
+import kotlinx.io.core.readUShort
+import net.mamoe.mirai.event.events.*
+import net.mamoe.mirai.internal.contact.impl
+import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
+import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
+import net.mamoe.mirai.internal.contact.toMiraiFriendInfo
+import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
+import net.mamoe.mirai.internal.network.components.PipelineContext
+import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
+import net.mamoe.mirai.internal.network.protocol.data.proto.FrdSysMsg
+import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
+import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x115.SubMsgType0x115
+import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
+import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44.Submsgtype0x44
+import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0xb3.SubMsgType0xb3
+import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList.GetFriendGroupList
+import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
+import net.mamoe.mirai.internal.utils._miraiContentToString
+import net.mamoe.mirai.internal.utils.io.serialization.loadAs
+import net.mamoe.mirai.utils.*
+
+/**
+ * All [FriendEvent] except [FriendMessageEvent]
+ *
+ * @see FriendInputStatusChangedEvent
+ * @see FriendAddEvent
+ * @see StrangerRelationChangeEvent.Friended
+ */
+internal class FriendNoticeProcessor(
+    private val logger: MiraiLogger,
+) : MixedNoticeProcessor(), NewContactSupport {
+    override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
+        if (msgHead.msgType != 191) return
+
+        var fromGroup = 0L
+        var pbNick = ""
+        msgBody.msgContent.read {
+            readUByte() // version
+            discardExact(readUByte().toInt()) //skip
+            readUShort() //source id
+            readUShort() //SourceSubID
+            discardExact(readUShort().toLong()) //skip size
+            if (readUShort().toInt() != 0) { //hasExtraInfo
+                discardExact(readUShort().toInt()) //mail address info, skip
+            }
+            discardExact(4 + readUShort().toInt()) //skip
+            for (i in 1..readUByte().toInt()) { //pb size
+                val type = readUShort().toInt()
+                val pbArray = ByteArray(readUShort().toInt() and 0xFF)
+                readAvailable(pbArray)
+                when (type) {
+                    1000 -> pbArray.loadAs(FrdSysMsg.GroupInfo.serializer()).let { fromGroup = it.groupUin }
+                    1002 -> pbArray.loadAs(FrdSysMsg.FriendMiscInfo.serializer())
+                        .let { pbNick = it.fromuinNick }
+                    else -> {
+                    } //ignore
+                }
+            }
+        }
+
+        msgHead.context {
+            if (fromUin == authUin) {
+                logger.error { "Could not determine uin since `fromUin` = `authUin` = $fromUin" }
+                return
+            }
+            val id = fromUin or authUin // 对方 qq
+            if (bot.getStranger(id) != null) return
+
+            val nick = fromNick.ifEmpty { authNick }.ifEmpty { pbNick }
+            collect(StrangerAddEvent(bot.addNewStranger(StrangerInfoImpl(id, nick, fromGroup)) ?: return))
+        }
+
+    }
+
+    override suspend fun PipelineContext.processImpl(data: MsgType0x210) = data.context {
+        when (data.uSubMsgType) {
+            0xB3L -> {
+                // 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: SubMsgType0xb3.MsgBody = vProtobuf.loadAs(SubMsgType0xb3.MsgBody.serializer())
+                handleFriendAddedB(data, body)
+            }
+            0x44L -> {
+                val body = vProtobuf.loadAs(Submsgtype0x44.MsgBody.serializer())
+                handleFriendAddedA(body)
+            }
+            0x27L -> {
+                val body = vProtobuf.loadAs(SubMsgType0x27MsgBody.serializer())
+                for (msgModInfo in body.msgModInfos) {
+                    when {
+                        msgModInfo.msgModFriendRemark != null -> handleRemarkChanged(msgModInfo.msgModFriendRemark)
+                        msgModInfo.msgDelFriend != null -> handleFriendDeleted(msgModInfo.msgDelFriend)
+                        msgModInfo.msgModCustomFace != null -> handleAvatarChanged(msgModInfo.msgModCustomFace)
+                        msgModInfo.msgModProfile != null -> handleProfileChanged(msgModInfo.msgModProfile)
+                    }
+                }
+            }
+            0x115L -> {
+                val body = vProtobuf.loadAs(SubMsgType0x115.MsgBody.serializer())
+                handleInputStatusChanged(body)
+            }
+            else -> return
+        }
+        markAsConsumed()
+    }
+
+    private fun PipelineContext.handleInputStatusChanged(body: SubMsgType0x115.MsgBody) {
+        val friend = bot.getFriend(body.fromUin) ?: return
+        val item = body.msgNotifyItem ?: return
+        collect(FriendInputStatusChangedEvent(friend, item.eventType == 1))
+    }
+
+    private fun PipelineContext.handleProfileChanged(body: ModProfile) {
+        var containsUnknown = false
+        for (profileInfo in body.msgProfileInfos) {
+            when (profileInfo.field) {
+                20002 -> { // 昵称修改
+                    val to = profileInfo.value
+                    if (body.uin == bot.id) {
+                        val from = bot.nick
+                        if (from == to) continue
+                        collect(BotNickChangedEvent(bot, from, to))
+                        bot.nick = to
+                    } else {
+                        val friend = bot.getFriend(body.uin)?.impl() ?: continue
+                        val from = bot.nick
+                        if (from == to) continue
+                        collect(FriendNickChangedEvent(friend, from, to))
+                        friend.info.nick = to
+                    }
+                }
+                else -> containsUnknown = true
+            }
+        }
+        if (body.msgProfileInfos.isEmpty() || containsUnknown) {
+            logger.debug { "Transformers528 0x27L: ProfileChanged new data: ${body._miraiContentToString()}" }
+        }
+    }
+
+    private fun PipelineContext.handleRemarkChanged(body: ModFriendRemark) {
+        for (new in body.msgFrdRmk) {
+            val friend = bot.getFriend(new.fuin)?.impl() ?: continue
+
+            // TODO: 2020/4/10 ADD REMARK QUERY
+            collect(FriendRemarkChangeEvent(friend, friend.remark, new.rmkName))
+            friend.info.remark = new.rmkName
+        }
+    }
+
+    private fun PipelineContext.handleAvatarChanged(body: ModCustomFace) {
+        if (body.uin == bot.id) {
+            collect(BotAvatarChangedEvent(bot))
+        }
+        collect(FriendAvatarChangedEvent(bot.getFriend(body.uin) ?: return))
+    }
+
+    private fun PipelineContext.handleFriendDeleted(body: DelFriend) {
+        for (id in body.uint64Uins) {
+            collect(FriendDeleteEvent(bot.removeFriend(id) ?: continue))
+        }
+    }
+
+    private suspend fun PipelineContext.handleFriendAddedA(
+        body: Submsgtype0x44.MsgBody,
+    ) = body.msgFriendMsgSync.context {
+        if (this == null) return
+
+        when (processtype) {
+            3, 9, 10 -> {
+                if (bot.getFriend(fuin) != null) return
+
+                val response = GetFriendGroupList.forSingleFriend(bot.client, fuin).sendAndExpect(bot)
+                val info = response.friendList.firstOrNull() ?: return
+                collect(
+                    FriendAddEvent(bot.addNewFriendAndRemoveStranger(info.toMiraiFriendInfo()) ?: return),
+                )
+
+            }
+        }
+    }
+
+    private fun PipelineContext.handleFriendAddedB(data: MsgType0x210, body: SubMsgType0xb3.MsgBody) = data.context {
+        val info = FriendInfoImpl(
+            uin = body.msgAddFrdNotify.fuin,
+            nick = body.msgAddFrdNotify.fuinNick,
+            remark = "",
+        )
+
+        val removed = bot.removeStranger(info.uin)
+        val added = bot.addNewFriendAndRemoveStranger(info) ?: return
+        collect(FriendAddEvent(added))
+        if (removed != null) collect(StrangerRelationChangeEvent.Friended(removed, added))
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/GroupEventProcessorContext.kt b/mirai-core/src/commonMain/kotlin/network/notice/GroupEventProcessorContext.kt
deleted file mode 100644
index f6d7a75df..000000000
--- a/mirai-core/src/commonMain/kotlin/network/notice/GroupEventProcessorContext.kt
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright 2019-2021 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.internal.network.notice
-
-import net.mamoe.mirai.Mirai
-import net.mamoe.mirai.contact.MemberPermission
-import net.mamoe.mirai.internal.QQAndroidBot
-import net.mamoe.mirai.internal.contact.GroupImpl
-import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
-import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
-import net.mamoe.mirai.internal.getGroupByUin
-import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
-import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
-import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
-
-internal interface GroupEventProcessorContext {
-
-    fun MsgComm.Msg.getNewMemberInfo(): MemberInfoImpl {
-        return MemberInfoImpl(
-            nameCard = msgHead.authNick.ifEmpty { msgHead.fromNick },
-            permission = MemberPermission.MEMBER,
-            specialTitle = "",
-            muteTimestamp = 0,
-            uin = msgHead.authUin,
-            nick = msgHead.authNick.ifEmpty { msgHead.fromNick },
-            remark = "",
-            anonymousId = null
-        )
-    }
-
-    suspend fun QQAndroidBot.createGroupForBot(groupUin: Long): GroupImpl? {
-        val group = getGroupByUin(groupUin)
-        if (group != null) {
-            return null
-        }
-
-        return getNewGroup(Mirai.calculateGroupCodeByGroupUin(groupUin))?.apply { groups.delegate.add(this) }
-    }
-
-    suspend fun QQAndroidBot.getNewGroup(groupCode: Long): GroupImpl? {
-        val troopNum = FriendList.GetTroopListSimplify(client)
-            .sendAndExpect(network, timeoutMillis = 10_000, retry = 5)
-            .groups.firstOrNull { it.groupCode == groupCode } ?: return null
-
-        return GroupImpl(
-            bot = this,
-            coroutineContext = coroutineContext,
-            id = groupCode,
-            groupInfo = GroupInfoImpl(troopNum),
-            members = Mirai.getRawGroupMemberList(
-                this,
-                troopNum.groupUin,
-                troopNum.groupCode,
-                troopNum.dwGroupOwnerUin
-            )
-        )
-    }
-
-}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/GroupListNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/GroupListNoticeProcessor.kt
index 034594158..0df4ca1e5 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/GroupListNoticeProcessor.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/GroupListNoticeProcessor.kt
@@ -39,6 +39,9 @@ import net.mamoe.mirai.utils.read
 
 
 /**
+ * Member/Bot invited/active join // force/active leave
+ * Member/Bot permission change
+ *
  * @see BotJoinGroupEvent
  * @see MemberJoinEvent
  *
@@ -47,10 +50,13 @@ import net.mamoe.mirai.utils.read
  *
  * @see MemberPermissionChangeEvent
  * @see BotGroupPermissionChangeEvent
+ *
+ * @see BotInvitedJoinGroupRequestEvent
+ * @see MemberJoinRequestEvent
  */
 internal class GroupListNoticeProcessor(
-    private val logger: MiraiLogger
-) : MixedNoticeProcessor(), GroupEventProcessorContext {
+    private val logger: MiraiLogger,
+) : MixedNoticeProcessor(), NewContactSupport {
 
     override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
         if (data.uSubMsgType != 0x44L) return
@@ -60,7 +66,7 @@ internal class GroupListNoticeProcessor(
         when (msg.msgGroupMsgSync.msgType) {
             1, 2 -> {
                 bot.components[ContactUpdater].groupListModifyLock.withLock {
-                    bot.createGroupForBot(Mirai.calculateGroupUinByGroupCode(msg.msgGroupMsgSync.grpCode))?.let {
+                    bot.addNewGroupByCode(msg.msgGroupMsgSync.grpCode)?.let {
                         collect(BotJoinGroupEvent.Active(it))
                     }
                 }
@@ -85,10 +91,10 @@ internal class GroupListNoticeProcessor(
             1 -> {
                 val dataList = message.parseToMessageDataList()
                 val invitor = dataList.first().let { messageData ->
-                    group.getOrFail(messageData.data.toLong())
+                    group[messageData.data.toLong()] ?: return
                 }
                 val member = dataList.last().let { messageData ->
-                    group.addNewNormalMember(messageData.toMemberInfo())
+                    group.addNewNormalMember(messageData.toMemberInfo()) ?: return
                 }
                 collect(MemberJoinEvent.Invite(member, invitor))
             }
@@ -116,50 +122,73 @@ internal class GroupListNoticeProcessor(
      * @see BotJoinGroupEvent.Active
      */
     override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
-        if (data.msgHead.msgType != 33) return
         bot.components[ContactUpdater].groupListModifyLock.withLock {
-            msgBody.msgContent.read {
-                val groupUin = Mirai.calculateGroupUinByGroupCode(readUInt().toLong())
-                val group =
-                    bot.getGroupByUin(groupUin) ?: bot.createGroupForBot(groupUin) ?: return markAsConsumed()
-                discardExact(1)
-                val joinedMemberUin = readUInt().toLong()
-                val joinType = readByte().toInt()
-                val invitorUin = readUInt().toLong()
-                when (joinType) {
-                    // 邀请加入
-                    -125, 3 -> {
-                        val invitor = group[invitorUin] ?: return markAsConsumed()
-                        collected += if (joinedMemberUin == bot.id) {
-                            BotJoinGroupEvent.Invite(invitor)
-                        } else {
-                            MemberJoinEvent.Invite(group.addNewNormalMember(getNewMemberInfo()), invitor)
-                        }
-                    }
-                    // 通过群员分享的二维码/直接加入
-                    -126, 2 -> {
-                        collected += if (joinedMemberUin == bot.id) {
-                            BotJoinGroupEvent.Active(group)
-                        } else {
-                            MemberJoinEvent.Active(group.addNewNormalMember(getNewMemberInfo()))
-                        }
-                    }
-                    // 忽略
-                    else -> {
+            when (data.msgHead.msgType) {
+                33 -> processGroupJoin33(data)
+                34 -> Unit // 34 与 33 重复, 忽略 34
+                38 -> processGroupJoin38(data)
+                85 -> processGroupJoin85(data)
+                else -> return
+            }
+            markAsConsumed()
+        }
+    }
+
+    // 33
+    private suspend fun PipelineContext.processGroupJoin33(data: MsgComm.Msg) = data.context {
+        msgBody.msgContent.read {
+            val groupUin = Mirai.calculateGroupUinByGroupCode(readUInt().toLong())
+            val group = bot.getGroupByUin(groupUin) ?: bot.addNewGroupByUin(groupUin) ?: return
+            discardExact(1)
+            val joinedMemberUin = readUInt().toLong()
+            val joinType = readByte().toInt()
+            val invitorUin = readUInt().toLong()
+            when (joinType) {
+                // 邀请加入
+                -125, 3 -> {
+                    val invitor = group[invitorUin] ?: return
+                    collected += if (joinedMemberUin == bot.id) {
+                        BotJoinGroupEvent.Invite(invitor)
+                    } else {
+                        MemberJoinEvent.Invite(group.addNewNormalMember(getNewMemberInfo()) ?: return, invitor)
                     }
                 }
+                // 通过群员分享的二维码/直接加入
+                -126, 2 -> {
+                    collected += if (joinedMemberUin == bot.id) {
+                        BotJoinGroupEvent.Active(group)
+                    } else {
+                        MemberJoinEvent.Active(group.addNewNormalMember(getNewMemberInfo()) ?: return)
+                    }
+                }
+                // 忽略
+                else -> {
+                }
             }
-            // 邀请入群
-            // package: 27 0B 60 E7 01 CA CC 69 8B 83 44 71 47 90 06 B9 DC C0 ED D4 B1 00 30 33 44 30 42 38 46 30 39 37 32 38 35 43 34 31 38 30 33 36 41 34 36 31 36 31 35 32 37 38 46 46 43 30 41 38 30 36 30 36 45 38 31 43 39 41 34 38 37
-            // package: groupUin + 01 CA CC 69 8B 83 + invitorUin + length(06) + string + magicKey
-
-
-            // 主动入群, 直接加入: msgContent=27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 42 39 41 30 33 45 38 34 30 39 34 42 46 30 45 32 45 38 42 31 43 43 41 34 32 42 38 42 44 42 35 34 44 42 31 44 32 32 30 46 30 38 39 46 46 35 41 38
-            // 主动直接加入                  27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 33 30 45 38 42 31 33 46 41 41 31 33 46 38 31 35 34 41 38 33 32 37 31 43 34 34 38 35 33 35 46 45 31 38 32 43 39 42 43 46 46 32 44 39 39 46 41 37
-
-            // 有人被邀请(经过同意后)加入      27 0B 60 E7 01 76 E4 B8 DD 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 34 30 34 38 32 33 38 35 37 41 37 38 46 33 45 37 35 38 42 39 38 46 43 45 44 43 32 41 30 31 36 36 30 34 31 36 39 35 39 30 38 39 30 39 45 31 34 34
-            // 搜索到群, 直接加入             27 0B 60 E7 01 07 6E 47 BA 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 32 30 39 39 42 39 41 46 32 39 41 35 42 33 46 34 32 30 44 36 44 36 39 35 44 38 45 34 35 30 46 30 45 30 38 45 31 41 39 42 46 46 45 32 30 32 34 35
         }
+        // 邀请入群
+        // package: 27 0B 60 E7 01 CA CC 69 8B 83 44 71 47 90 06 B9 DC C0 ED D4 B1 00 30 33 44 30 42 38 46 30 39 37 32 38 35 43 34 31 38 30 33 36 41 34 36 31 36 31 35 32 37 38 46 46 43 30 41 38 30 36 30 36 45 38 31 43 39 41 34 38 37
+        // package: groupUin + 01 CA CC 69 8B 83 + invitorUin + length(06) + string + magicKey
+
+
+        // 主动入群, 直接加入: msgContent=27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 42 39 41 30 33 45 38 34 30 39 34 42 46 30 45 32 45 38 42 31 43 43 41 34 32 42 38 42 44 42 35 34 44 42 31 44 32 32 30 46 30 38 39 46 46 35 41 38
+        // 主动直接加入                  27 0B 60 E7 01 76 E4 B8 DD 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 33 30 45 38 42 31 33 46 41 41 31 33 46 38 31 35 34 41 38 33 32 37 31 43 34 34 38 35 33 35 46 45 31 38 32 43 39 42 43 46 46 32 44 39 39 46 41 37
+
+        // 有人被邀请(经过同意后)加入      27 0B 60 E7 01 76 E4 B8 DD 83 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 34 30 34 38 32 33 38 35 37 41 37 38 46 33 45 37 35 38 42 39 38 46 43 45 44 43 32 41 30 31 36 36 30 34 31 36 39 35 39 30 38 39 30 39 45 31 34 34
+        // 搜索到群, 直接加入             27 0B 60 E7 01 07 6E 47 BA 82 3E 03 3F A2 06 B4 B4 BD A8 D5 DF 00 30 32 30 39 39 42 39 41 46 32 39 41 35 42 33 46 34 32 30 44 36 44 36 39 35 44 38 45 34 35 30 46 30 45 30 38 45 31 41 39 42 46 46 45 32 30 32 34 35
+    }
+
+    // 38
+    private suspend fun PipelineContext.processGroupJoin38(data: MsgComm.Msg) = data.context {
+        if (bot.getGroupByUin(msgHead.fromUin) != null) return
+        bot.addNewGroupByUin(msgHead.fromUin)?.let { collect(BotJoinGroupEvent.Active(it)) }
+    }
+
+    // 85
+    private suspend fun PipelineContext.processGroupJoin85(data: MsgComm.Msg) = data.context {
+        // msgHead.authUin: 处理人
+        if (msgHead.toUin != bot.id) return
+        processGroupJoin38(data)
     }
 
     ///////////////////////////////////////////////////////////////////////////
@@ -201,10 +230,10 @@ internal class GroupListNoticeProcessor(
             }
             2 -> { // 被邀请入群, 自动同意, 不需处理
 
-//                            val group = bot.getNewGroup(groupCode) ?: return null
-//                            val invitor = group[actionUin]
-//
-//                            BotJoinGroupEvent.Invite(invitor)
+                //                            val group = bot.getNewGroup(groupCode) ?: return null
+                //                            val invitor = group[actionUin]
+                //
+                //                            BotJoinGroupEvent.Invite(invitor)
             }
             3 -> { // 已被请他管理员处理
             }
@@ -250,6 +279,13 @@ internal class GroupListNoticeProcessor(
         markAsConsumed()
         when (data.msgType) {
             44 -> data.msgData.read {
+                //                  3D C4 33 DD 01 FF CD 76 F4 03 C3 7E 2E 34
+                //      群转让
+                //      start with  3D C4 33 DD 01 FF
+                //                  3D C4 33 DD 01 FF C3 7E 2E 34 CD 76 F4 03
+                // 权限变更
+                //                  3D C4 33 DD 01 00/01 .....
+                //                  3D C4 33 DD 01 01 C3 7E 2E 34 01
                 discardExact(5)
                 val kind = readUByte().toInt()
                 if (kind == 0xFF) {
@@ -327,7 +363,7 @@ toUin=0x0000000026BA1173(649728371)
         target: Long,
         kind: Int,
         operator: Long,
-        groupUin: Long
+        groupUin: Long,
     ) {
         when (kind) {
             2, 0x82 -> bot.getGroupByUin(groupUin)?.let { group ->
@@ -367,7 +403,7 @@ toUin=0x0000000026BA1173(649728371)
     private fun PipelineContext.handlePermissionChange(
         data: OnlinePushTrans.PbMsgInfo,
         target: Long,
-        newPermissionByte: Int
+        newPermissionByte: Int,
     ) {
         val group = bot.getGroupByUin(data.fromUin) ?: return
 
@@ -410,7 +446,7 @@ toUin=0x0000000026BA1173(649728371)
             // member Retrieve or permission changed to OWNER
             var newOwner = group[to]
             if (newOwner == null) {
-                newOwner = group.addNewNormalMember(MemberInfoImpl(uin = to, nick = "", permission = OWNER))
+                newOwner = group.addNewNormalMember(MemberInfoImpl(uin = to, nick = "", permission = OWNER)) ?: return
                 collect(MemberJoinEvent.Retrieve(newOwner))
             } else if (newOwner.permission != OWNER) {
                 collect(MemberPermissionChangeEvent(newOwner, newOwner.permission, OWNER))
@@ -421,7 +457,7 @@ toUin=0x0000000026BA1173(649728371)
 
             // bot Retrieve or permission changed to OWNER
             if (group == null) {
-                collect(BotJoinGroupEvent.Retrieve(bot.createGroupForBot(data.fromUin)!!))
+                collect(BotJoinGroupEvent.Retrieve(bot.addNewGroupByUin(data.fromUin) ?: return))
                 return
             }
 
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/GroupMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/GroupMessageProcessor.kt
index 4a8ff2834..e10e1414d 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/GroupMessageProcessor.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/GroupMessageProcessor.kt
@@ -35,6 +35,9 @@ import net.mamoe.mirai.internal.utils.io.serialization.loadAs
 import net.mamoe.mirai.message.data.MessageSourceKind
 import net.mamoe.mirai.utils.*
 
+/**
+ * Handles [GroupMessageEvent]. For private message events, see [PrivateMessageNoticeProcessor]
+ */
 internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPushMsg>(type()) {
     internal data class SendGroupMessageReceipt(
         val messageRandom: Int,
@@ -61,7 +64,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
     }
 
 
-    override suspend fun PipelineContext.process0(data: MsgOnlinePush.PbPushMsg) {
+    override suspend fun PipelineContext.processImpl(data: MsgOnlinePush.PbPushMsg) {
         val msgHead = data.msg.msgHead
 
         val isFromSelfAccount = msgHead.fromUin == bot.id
@@ -73,13 +76,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
                 // 3116=group music share
                 // 2021=group file
                 // message sent by bot
-                collect(
-                    SendGroupMessageReceipt(
-                        messageRandom,
-                        msgHead.msgSeq,
-                        msgHead.fromAppid
-                    )
-                )
+                collect(SendGroupMessageReceipt(messageRandom, msgHead.msgSeq, msgHead.fromAppid))
                 return
             }
             // else: sync form other device
@@ -132,7 +129,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
                     group = group,
                     sender = sender,
                     senderName = nameCard.nick,
-                )
+                ),
             )
             return
         } else {
@@ -145,8 +142,8 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
                     sender = sender,
                     message = msgs.map { it.msg }.toMessageChainOnline(bot, group.id, MessageSourceKind.GROUP),
                     permission = sender.permission,
-                    time = msgHead.msgTime
-                )
+                    time = msgHead.msgTime,
+                ),
             )
             return
         }
@@ -154,7 +151,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
 
     private suspend inline fun broadcastNameCardChangedEventIfNecessary(
         sender: Member,
-        new: MemberNick
+        new: MemberNick,
     ) {
         if (sender is NormalMemberImpl) {
             val currentNameCard = sender.nameCard
@@ -177,7 +174,7 @@ internal class GroupMessageProcessor : SimpleNoticeProcessor<MsgOnlinePush.PbPus
 
     private fun findSenderName(
         extraInfo: ImMsgBody.ExtraInfo?,
-        groupInfo: MsgComm.GroupInfo
+        groupInfo: MsgComm.GroupInfo,
     ): MemberNick? =
         extraInfo?.groupCard?.takeIf { it.isNotEmpty() }?.decodeCommCardNameBuf()?.let {
             MemberNick(it, true)
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/NewContactSupport.kt b/mirai-core/src/commonMain/kotlin/network/notice/NewContactSupport.kt
new file mode 100644
index 000000000..ac829ca40
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/notice/NewContactSupport.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2019-2021 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.internal.network.notice
+
+import kotlinx.coroutines.cancel
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.contact.MemberPermission
+import net.mamoe.mirai.internal.QQAndroidBot
+import net.mamoe.mirai.internal.contact.FriendImpl
+import net.mamoe.mirai.internal.contact.GroupImpl
+import net.mamoe.mirai.internal.contact.StrangerImpl
+import net.mamoe.mirai.internal.contact.impl
+import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
+import net.mamoe.mirai.internal.contact.info.GroupInfoImpl
+import net.mamoe.mirai.internal.contact.info.MemberInfoImpl
+import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
+import net.mamoe.mirai.internal.getGroupByUin
+import net.mamoe.mirai.internal.network.protocol.data.jce.StTroopNum
+import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
+import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
+import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
+
+internal interface NewContactSupport {
+
+    fun MsgComm.Msg.getNewMemberInfo(): MemberInfoImpl {
+        return MemberInfoImpl(
+            nameCard = msgHead.authNick.ifEmpty { msgHead.fromNick },
+            permission = MemberPermission.MEMBER,
+            specialTitle = "",
+            muteTimestamp = 0,
+            uin = msgHead.authUin,
+            nick = msgHead.authNick.ifEmpty { msgHead.fromNick },
+            remark = "",
+            anonymousId = null,
+        )
+    }
+
+    suspend fun QQAndroidBot.addNewGroupByCode(code: Long): GroupImpl? {
+        if (getGroup(code) != null) return null
+        return getNewGroup(code)?.apply { groups.delegate.add(this) }
+    }
+
+    suspend fun QQAndroidBot.addNewGroupByUin(groupUin: Long): GroupImpl? {
+        if (getGroupByUin(groupUin) != null) return null
+        return getNewGroup(Mirai.calculateGroupCodeByGroupUin(groupUin))?.apply { groups.delegate.add(this) }
+    }
+
+    suspend fun QQAndroidBot.addNewGroup(stTroopNum: StTroopNum): GroupImpl? {
+        if (getGroup(stTroopNum.groupCode) != null) return null
+        return getNewGroup(stTroopNum)?.apply { groups.delegate.add(this) }
+    }
+
+    fun QQAndroidBot.removeStranger(id: Long): StrangerImpl? {
+        val instance = strangers[id] ?: return null
+        strangers.remove(instance.id)
+        instance.cancel()
+        return instance
+    }
+
+    fun QQAndroidBot.removeFriend(id: Long): FriendImpl? {
+        val instance = friends[id] ?: return null
+        friends.remove(instance.id)
+        instance.cancel()
+        return instance
+    }
+
+    fun QQAndroidBot.addNewFriendAndRemoveStranger(info: FriendInfoImpl): FriendImpl? {
+        if (friends.contains(info.uin)) return null
+        strangers[info.uin]?.let { removeStranger(it.id) }
+        val friend = Mirai.newFriend(bot, info).impl()
+        friends.delegate.add(friend)
+        return friend
+    }
+
+    fun QQAndroidBot.addNewStranger(info: StrangerInfoImpl): StrangerImpl? {
+        if (friends.contains(info.uin)) return null // cannot have both stranger and friend
+        if (strangers.contains(info.uin)) return null
+        val stranger = Mirai.newStranger(bot, info).impl()
+        strangers.delegate.add(stranger)
+        return stranger
+    }
+
+    private suspend fun QQAndroidBot.getNewGroup(groupCode: Long): GroupImpl? {
+        val troopNum = FriendList.GetTroopListSimplify(client)
+            .sendAndExpect(network, timeoutMillis = 10_000, retry = 5)
+            .groups.firstOrNull { it.groupCode == groupCode } ?: return null
+
+        return getNewGroup(troopNum)
+    }
+
+    private suspend fun QQAndroidBot.getNewGroup(troopNum: StTroopNum): GroupImpl? {
+        return GroupImpl(
+            bot = this,
+            parentCoroutineContext = coroutineContext,
+            id = troopNum.groupCode,
+            groupInfo = GroupInfoImpl(troopNum),
+            members = Mirai.getRawGroupMemberList(
+                this,
+                troopNum.groupUin,
+                troopNum.groupCode,
+                troopNum.dwGroupOwnerUin,
+            ),
+        )
+    }
+
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/OtherClientNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/OtherClientNoticeProcessor.kt
new file mode 100644
index 000000000..ad8d6622d
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/notice/OtherClientNoticeProcessor.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2019-2021 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.internal.network.notice
+
+import kotlinx.coroutines.CancellationException
+import kotlinx.coroutines.cancel
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.sync.withLock
+import net.mamoe.mirai.Mirai
+import net.mamoe.mirai.contact.ClientKind
+import net.mamoe.mirai.contact.OtherClientInfo
+import net.mamoe.mirai.contact.Platform
+import net.mamoe.mirai.event.events.OtherClientMessageEvent
+import net.mamoe.mirai.event.events.OtherClientOfflineEvent
+import net.mamoe.mirai.event.events.OtherClientOnlineEvent
+import net.mamoe.mirai.internal.contact.appId
+import net.mamoe.mirai.internal.contact.createOtherClient
+import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
+import net.mamoe.mirai.internal.message.contextualBugReportException
+import net.mamoe.mirai.internal.network.components.ContactUpdater
+import net.mamoe.mirai.internal.network.components.MixedNoticeProcessor
+import net.mamoe.mirai.internal.network.components.PipelineContext
+import net.mamoe.mirai.internal.network.handler.logger
+import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
+import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
+import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7
+import net.mamoe.mirai.internal.utils._miraiContentToString
+import net.mamoe.mirai.internal.utils.io.serialization.loadAs
+import net.mamoe.mirai.message.data.PlainText
+import net.mamoe.mirai.message.data.buildMessageChain
+import net.mamoe.mirai.utils.context
+
+/**
+ * @see OtherClientOnlineEvent
+ * @see OtherClientOfflineEvent
+ *
+ * @see OtherClientMessageEvent
+ */
+internal class OtherClientNoticeProcessor : MixedNoticeProcessor() {
+    /**
+     * @see OtherClientOnlineEvent
+     * @see OtherClientOfflineEvent
+     */
+    override suspend fun PipelineContext.processImpl(data: RequestPushStatus) {
+        markAsConsumed()
+        bot.components[ContactUpdater].otherClientsLock.withLock {
+            val instanceInfo = data.vecInstanceList?.firstOrNull()
+            val appId = instanceInfo?.iAppId ?: 1
+            when (data.status.toInt()) {
+                1 -> { // online
+                    if (bot.otherClients.any { appId == it.appId }) return
+
+                    suspend fun tryFindInQuery(): OtherClientInfo? {
+                        return Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
+                            ?: kotlin.run {
+                                delay(2000) // sometimes server sync slow
+                                Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
+                            }
+                    }
+
+                    val info =
+                        tryFindInQuery() ?: kotlin.run {
+                            bot.network.logger.warning(
+                                contextualBugReportException(
+                                    "SvcRequestPushStatus (OtherClient online)",
+                                    "packet: \n" + data._miraiContentToString() +
+                                            "\n\nquery: \n" +
+                                            Mirai.getOnlineOtherClientsList(bot)._miraiContentToString(),
+                                    additional = "Failed to find corresponding instanceInfo.",
+                                ),
+                            )
+                            OtherClientInfo(appId, Platform.WINDOWS, "", "电脑")
+                        }
+
+                    val client = bot.createOtherClient(info)
+                    bot.otherClients.delegate.add(client)
+                    collected += OtherClientOnlineEvent(
+                        client,
+                        ClientKind[data.nClientType?.toInt() ?: 0],
+                    )
+                }
+
+                2 -> { // off
+                    val client = bot.otherClients.find { it.appId == appId } ?: return
+                    client.cancel(CancellationException("Offline"))
+                    bot.otherClients.delegate.remove(client)
+                    collected += OtherClientOfflineEvent(client)
+                }
+
+                else -> throw contextualBugReportException(
+                    "decode SvcRequestPushStatus (PC Client status change)",
+                    data._miraiContentToString(),
+                    additional = "unknown status=${data.status}",
+                )
+            }
+        }
+    }
+
+
+    /**
+     * @see OtherClientMessageEvent
+     */
+    override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
+        if (msgHead.msgType != 529) return
+        markAsConsumed() // todo check
+        if (msgHead.c2cCmd != 7) return
+        val body = msgBody.msgContent.loadAs(SubMsgType0x7.MsgBody.serializer())
+
+        val textMsg =
+            body.msgSubcmd0x4Generic?.buf?.loadAs(SubMsgType0x7.MsgBody.QQDataTextMsg.serializer())
+                ?: return
+
+        with(body.msgHeader ?: return) {
+            if (dstUin != bot.id) return
+            val client = bot.otherClients.find { it.appId == srcInstId }
+                ?: return // don't compare with dstAppId. diff.
+
+            val chain = buildMessageChain {
+                +OnlineMessageSourceFromFriendImpl(bot, listOf(data))
+                for (msgItem in textMsg.msgItems) {
+                    when (msgItem.type) {
+                        1 -> +PlainText(msgItem.text)
+                        else -> {
+                        }
+                    }
+                }
+            }
+
+            collect(OtherClientMessageEvent(client, chain, msgHead.msgTime))
+        }
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/PrivateMessageNoticeProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/PrivateMessageNoticeProcessor.kt
new file mode 100644
index 000000000..33a86edf9
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/network/notice/PrivateMessageNoticeProcessor.kt
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2019-2021 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.internal.network.notice
+
+import net.mamoe.mirai.contact.User
+import net.mamoe.mirai.event.events.*
+import net.mamoe.mirai.internal.contact.*
+import net.mamoe.mirai.internal.getGroupByUin
+import net.mamoe.mirai.internal.message.toMessageChainOnline
+import net.mamoe.mirai.internal.network.components.PipelineContext
+import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
+import net.mamoe.mirai.internal.network.components.SsoProcessor
+import net.mamoe.mirai.internal.network.notice.SystemMessageProcessor.Companion.fromSync
+import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
+import net.mamoe.mirai.utils.context
+
+/**
+ * Handles [UserMessageEvent] and their sync events. For [GroupMessageEvent], see [GroupMessageProcessor]
+ *
+ * @see StrangerMessageEvent
+ * @see StrangerMessageSyncEvent
+ *
+ * @see FriendMessageEvent
+ * @see FriendMessageSyncEvent
+ *
+ * @see GroupTempMessageEvent
+ * @see GroupTempMessageSyncEvent
+ */
+internal class PrivateMessageNoticeProcessor : SimpleNoticeProcessor<MsgComm.Msg>(type()) {
+    override suspend fun PipelineContext.processImpl(data: MsgComm.Msg) = data.context {
+        if (msgHead.fromUin == bot.id && fromSync) {
+            // Bot send message to himself? or from other client? I am not the implementer.
+            bot.client.sendFriendMessageSeq.updateIfSmallerThan(msgHead.msgSeq)
+            return
+        }
+        if (!bot.components[SsoProcessor].firstLoginSucceed) return
+        val senderUin = if (fromSync) msgHead.toUin else msgHead.fromUin
+        when (msgHead.msgType) {
+            166, 167, // 单向好友
+            208, // friend ptt, maybe also support stranger
+            -> {
+                handlePrivateMessage(data, bot.getFriend(senderUin) ?: bot.getStranger(senderUin) ?: return)
+                markAsConsumed()
+            }
+
+            141, // group temp
+            -> {
+                val tmpHead = msgHead.c2cTmpMsgHead ?: return
+                val group = bot.getGroupByUin(tmpHead.groupUin) ?: return
+                handlePrivateMessage(data, group[senderUin] ?: return)
+                markAsConsumed()
+            }
+        }
+
+    }
+
+    private suspend fun PipelineContext.handlePrivateMessage(
+        data: MsgComm.Msg,
+        user: User,
+    ) = data.context {
+        user.impl()
+        if (!user.messageSeq.updateIfDifferentWith(msgHead.msgSeq)) return
+        if (contentHead?.autoReply == 1) return
+
+        val msgs = user.fragmentedMessageMerger.tryMerge(this)
+        if (msgs.isEmpty()) return
+
+        val chain = msgs.toMessageChainOnline(bot, 0, user.correspondingMessageSourceKind)
+        val time = msgHead.msgTime
+
+        collected += if (fromSync) {
+            when (user) {
+                is FriendImpl -> FriendMessageSyncEvent(user, chain, time)
+                is StrangerImpl -> StrangerMessageSyncEvent(user, chain, time)
+                is NormalMemberImpl -> GroupTempMessageSyncEvent(user, chain, time)
+                else -> null
+            }
+        } else {
+            when (user) {
+                is FriendImpl -> FriendMessageEvent(user, chain, time)
+                is StrangerImpl -> StrangerMessageEvent(user, chain, time)
+                is NormalMemberImpl -> GroupTempMessageEvent(user, chain, time)
+                else -> null
+            }
+        } ?: error("unreachable")
+    }
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/SystemMessageProcessor.kt b/mirai-core/src/commonMain/kotlin/network/notice/SystemMessageProcessor.kt
index d9ffb3c4f..f36b71e20 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/SystemMessageProcessor.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/SystemMessageProcessor.kt
@@ -10,39 +10,24 @@
 package net.mamoe.mirai.internal.network.notice
 
 import kotlinx.coroutines.sync.withLock
-import kotlinx.io.core.discardExact
-import kotlinx.io.core.readUByte
-import kotlinx.io.core.readUShort
-import net.mamoe.mirai.Mirai
-import net.mamoe.mirai.event.broadcast
-import net.mamoe.mirai.event.events.*
-import net.mamoe.mirai.internal.contact.*
-import net.mamoe.mirai.internal.contact.info.StrangerInfoImpl
-import net.mamoe.mirai.internal.getGroupByUin
-import net.mamoe.mirai.internal.message.OnlineMessageSourceFromFriendImpl
-import net.mamoe.mirai.internal.message.toMessageChainOnline
 import net.mamoe.mirai.internal.network.components.ContactUpdater
 import net.mamoe.mirai.internal.network.components.MsgCommonMsgProcessor
 import net.mamoe.mirai.internal.network.components.PipelineContext
 import net.mamoe.mirai.internal.network.components.SsoProcessor
 import net.mamoe.mirai.internal.network.handler.logger
-import net.mamoe.mirai.internal.network.protocol.data.proto.FrdSysMsg
 import net.mamoe.mirai.internal.network.protocol.data.proto.MsgComm
-import net.mamoe.mirai.internal.network.protocol.data.proto.SubMsgType0x7
 import net.mamoe.mirai.internal.network.protocol.packet.chat.NewContact
-import net.mamoe.mirai.internal.utils.io.serialization.loadAs
-import net.mamoe.mirai.message.data.MessageSourceKind
-import net.mamoe.mirai.message.data.PlainText
-import net.mamoe.mirai.message.data.buildMessageChain
-import net.mamoe.mirai.utils.*
+import net.mamoe.mirai.utils.TypeKey
+import net.mamoe.mirai.utils.debug
+import net.mamoe.mirai.utils.toUHexString
 
-internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProcessorContext {
+internal class SystemMessageProcessor : MsgCommonMsgProcessor(), NewContactSupport {
     companion object {
         val KEY_FROM_SYNC = TypeKey<Boolean>("fromSync")
         val PipelineContext.fromSync get() = attributes[KEY_FROM_SYNC]
     }
 
-    override suspend fun PipelineContext.process0(data: MsgComm.Msg): Unit = data.run {
+    override suspend fun PipelineContext.processImpl(data: MsgComm.Msg): Unit = data.run {
         // TODO: 2021/6/26 extract logic into multiple processors
         when (msgHead.msgType) {
             33 -> bot.components[ContactUpdater].groupListModifyLock.withLock {
@@ -53,19 +38,11 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
             }
 
             38 -> bot.components[ContactUpdater].groupListModifyLock.withLock { // 建群
-                bot.createGroupForBot(msgHead.fromUin)
-                    ?.let { collect(BotJoinGroupEvent.Active(it)) }
-                return
+                TODO("removed")
             }
 
             85 -> bot.components[ContactUpdater].groupListModifyLock.withLock { // 其他客户端入群
-                // msgHead.authUin: 处理人
-
-                if (msgHead.toUin == bot.id) {
-                    bot.createGroupForBot(msgHead.fromUin)
-                        ?.let { collect(BotJoinGroupEvent.Active(it)) }
-                }
-                return
+                TODO("removed")
             }
 
             /*
@@ -116,92 +93,11 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
 
             //167 单向好友
             166, 167 -> {
-                //我也不知道为什么要这样写,但它就是能跑
-                if (msgHead.fromUin == bot.id && fromSync) {
-                    loop@ while (true) {
-                        val instance = bot.client.getFriendSeq()
-                        if (instance < msgHead.msgSeq) {
-                            if (bot.client.setFriendSeq(instance, msgHead.msgSeq)) {
-                                break@loop
-                            }
-                        } else break@loop
-                    }
-                    return
-                }
-                if (!bot.components[SsoProcessor].firstLoginSucceed) {
-                    return
-                }
-                val fromUin = if (fromSync) {
-                    msgHead.toUin
-                } else {
-                    msgHead.fromUin
-                }
-                bot.getFriend(fromUin)?.let { friend ->
-                    friend.checkIsFriendImpl()
-                    friend.lastMessageSequence.loop {
-                        //我也不知道为什么要这样写,但它就是能跑
-                        if (friend.lastMessageSequence.value != msgHead.msgSeq
-                            && friend.lastMessageSequence.compareAndSet(it, msgHead.msgSeq)
-                            && contentHead?.autoReply != 1
-                        ) {
-                            val msgs = friend.friendPkgMsgParsingCache.tryMerge(this)
-                            if (msgs.isNotEmpty()) {
-                                collect(
-                                    if (fromSync) {
-                                        FriendMessageSyncEvent(
-                                            friend,
-                                            msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND),
-                                            msgHead.msgTime
-                                        )
-                                    } else {
-                                        FriendMessageEvent(
-                                            friend,
-                                            msgs.toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND),
-                                            msgHead.msgTime
-                                        )
-                                    }
-                                )
-                            } else return
-                        }
-                        return
-                    }
-                } ?: bot.getStranger(fromUin)?.let { stranger ->
-                    stranger.checkIsImpl()
-                    stranger.lastMessageSequence.loop {
-                        //我也不知道为什么要这样写,但它就是能跑
-                        if (stranger.lastMessageSequence.value != msgHead.msgSeq && stranger.lastMessageSequence.compareAndSet(
-                                it,
-                                msgHead.msgSeq
-                            ) && contentHead?.autoReply != 1
-                        ) {
-                            collect(
-                                if (fromSync) {
-                                    StrangerMessageSyncEvent(
-                                        stranger,
-                                        listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.STRANGER),
-                                        msgHead.msgTime
-                                    )
-                                } else {
-                                    StrangerMessageEvent(
-                                        stranger,
-                                        listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.STRANGER),
-                                        msgHead.msgTime
-                                    )
-                                }
-                            )
-                        }
-                        return
-                    }
-                }
-                return
+                TODO("removed")
             }
             208 -> {
                 // friend ptt
-                val target = bot.getFriend(msgHead.fromUin) ?: return
-                val lsc = listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.FRIEND)
-
-                collect(FriendMessageEvent(target, lsc, msgHead.msgTime))
-                return
+                TODO("removed")
             }
             529 -> {
 
@@ -210,30 +106,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
                 when (msgHead.c2cCmd) {
                     // other client sync
                     7 -> {
-                        val body = msgBody.msgContent.loadAs(SubMsgType0x7.MsgBody.serializer())
-
-                        val textMsg =
-                            body.msgSubcmd0x4Generic?.buf?.loadAs(SubMsgType0x7.MsgBody.QQDataTextMsg.serializer())
-                                ?: return
-
-                        with(body.msgHeader ?: return) {
-                            if (dstUin != bot.id) return
-                            val client = bot.otherClients.find { it.appId == srcInstId }
-                                ?: return// don't compare with dstAppId. diff.
-
-                            val chain = buildMessageChain {
-                                +OnlineMessageSourceFromFriendImpl(bot, listOf(data))
-                                for (msgItem in textMsg.msgItems) {
-                                    when (msgItem.type) {
-                                        1 -> +PlainText(msgItem.text)
-                                        else -> {
-                                        }
-                                    }
-                                }
-                            }
-
-                            collect(OtherClientMessageEvent(client, chain, msgHead.msgTime))
-                        }
+                        TODO("removed")
                     }
                 }
 
@@ -245,39 +118,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
                 if (!bot.components[SsoProcessor].firstLoginSucceed || msgHead.fromUin == bot.id && !fromSync) {
                     return
                 }
-                val tmpHead = msgHead.c2cTmpMsgHead ?: return
-                val member = bot.getGroupByUin(tmpHead.groupUin)?.get(
-                    if (fromSync) {
-                        msgHead.toUin
-                    } else {
-                        msgHead.fromUin
-                    }
-                )
-                    ?: return
-
-                member.checkIsMemberImpl()
-
-                member.lastMessageSequence.loop { instant ->
-                    if (member.lastMessageSequence.value != msgHead.msgSeq && contentHead?.autoReply != 1) {
-                        if (member.lastMessageSequence.compareAndSet(instant, msgHead.msgSeq)) {
-                            collect(
-                                if (fromSync) {
-                                    GroupTempMessageSyncEvent(
-                                        member,
-                                        listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
-                                        msgHead.msgTime
-                                    )
-                                } else {
-                                    GroupTempMessageEvent(
-                                        member,
-                                        listOf(this).toMessageChainOnline(bot, 0, MessageSourceKind.TEMP),
-                                        msgHead.msgTime
-                                    )
-                                }
-                            )
-                        }
-                    } else return
-                }
+                TODO("removed")
             }
             84, 87 -> { // 请求入群验证 和 被要求入群
                 bot.network.run {
@@ -298,45 +139,7 @@ internal class SystemMessageProcessor : MsgCommonMsgProcessor(), GroupEventProce
             }
             //陌生人添加信息
             191 -> {
-                var fromGroup = 0L
-                var pbNick = ""
-                msgBody.msgContent.read {
-                    readUByte()// version
-                    discardExact(readUByte().toInt())//skip
-                    readUShort()//source id
-                    readUShort()//SourceSubID
-                    discardExact(readUShort().toLong())//skip size
-                    if (readUShort().toInt() != 0) {//hasExtraInfo
-                        discardExact(readUShort().toInt())//mail address info, skip
-                    }
-                    discardExact(4 + readUShort().toInt())//skip
-                    for (i in 1..readUByte().toInt()) {//pb size
-                        val type = readUShort().toInt()
-                        val pbArray = ByteArray(readUShort().toInt() and 0xFF)
-                        readAvailable(pbArray)
-                        when (type) {
-                            1000 -> pbArray.loadAs(FrdSysMsg.GroupInfo.serializer()).let { fromGroup = it.groupUin }
-                            1002 -> pbArray.loadAs(FrdSysMsg.FriendMiscInfo.serializer())
-                                .let { pbNick = it.fromuinNick }
-                            else -> {
-                            }//ignore
-                        }
-                    }
-                }
-                val nick =
-                    sequenceOf(msgHead.fromNick, msgHead.authNick, pbNick).filter { it.isNotEmpty() }.firstOrNull()
-                        ?: return
-                val id =
-                    sequenceOf(msgHead.fromUin, msgHead.authUin).filter { it != 0L }.firstOrNull() ?: return//对方QQ
-                Mirai.newStranger(bot, StrangerInfoImpl(id, nick, fromGroup)).checkIsImpl().let {
-                    bot.getStranger(id)?.let { previous ->
-                        bot.strangers.remove(id)
-                        StrangerRelationChangeEvent.Deleted(previous).broadcast()
-                    }
-                    bot.strangers.delegate.add(it)
-
-                    collect(StrangerAddEvent(it))
-                }
+                TODO("removed")
             }
             // 732:  27 0B 60 E7 0C 01 3E 03 3F A2 5E 90 60 E2 00 01 44 71 47 90 00 00 02 58
             // 732:  27 0B 60 E7 11 00 40 08 07 20 E7 C1 AD B8 02 5A 36 08 B4 E7 E0 F0 09 1A 1A 08 9C D4 16 10 F7 D2 D8 F5 05 18 D0 E2 85 F4 06 20 00 28 00 30 B4 E7 E0 F0 09 2A 0E 08 00 12 0A 08 9C D4 16 10 00 18 01 20 00 30 00 38 00
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt
index a7e379cc8..9c76c15f3 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgInfoDecoder.kt
@@ -30,7 +30,7 @@ import net.mamoe.mirai.utils.toUHexString
 internal class MsgInfoDecoder(
     private val logger: MiraiLogger,
 ) : SimpleNoticeProcessor<MsgInfo>(type()) {
-    override suspend fun PipelineContext.process0(data: MsgInfo) {
+    override suspend fun PipelineContext.processImpl(data: MsgInfo) {
         when (data.shMsgType.toUShort().toInt()) {
             // 528
             0x210 -> fire(data.vMsg.loadAs(MsgType0x210.serializer()))
diff --git a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgType0x210Decoder.kt b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgType0x210Decoder.kt
index 7b91a4de9..f5142a7d5 100644
--- a/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgType0x210Decoder.kt
+++ b/mirai-core/src/commonMain/kotlin/network/notice/decoders/MsgType0x210Decoder.kt
@@ -14,7 +14,7 @@ import net.mamoe.mirai.internal.network.components.SimpleNoticeProcessor
 import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
 
 internal class MsgType0x210Decoder : SimpleNoticeProcessor<MsgType0x210>(type()) {
-    override suspend fun PipelineContext.process0(data: MsgType0x210) {
+    override suspend fun PipelineContext.processImpl(data: MsgType0x210) {
         when (data.uSubMsgType) {
             0x8AL -> {
             }
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt
index 3ccac2f02..145cefb7d 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/jce/ReqPushStatus.kt
@@ -10,6 +10,7 @@
 package net.mamoe.mirai.internal.network.protocol.data.jce
 
 import kotlinx.serialization.Serializable
+import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.utils.io.JceStruct
 import net.mamoe.mirai.internal.utils.io.serialization.tars.TarsId
 
@@ -25,6 +26,5 @@ internal class RequestPushStatus(
     @JvmField @TarsId(6) val nClientType: Long? = null,
     @JvmField @TarsId(7) val nInstanceId: Long? = null,
     @JvmField @TarsId(8) val vecInstanceList: List<InstanceInfo>? = null,
-) : JceStruct
-
+) : JceStruct, Packet
 
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt
index 8e53e9912..95db783d1 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/data/proto/msgType0x210.kt
@@ -952,7 +952,7 @@ internal class Submsgtype0x27 {
         @Serializable
         internal class ProfileInfo(
             @ProtoNumber(1) @JvmField val field: Int = 0,
-            @ProtoNumber(2) @JvmField val value: ByteArray = EMPTY_BYTE_ARRAY,
+            @ProtoNumber(2) @JvmField val value: String = "",
         ) : ProtoBuf
 
         @Serializable
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt
index dce8b1e51..c1d737596 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/PacketFactory.kt
@@ -13,6 +13,7 @@ import kotlinx.io.core.ByteReadPacket
 import net.mamoe.mirai.event.Event
 import net.mamoe.mirai.internal.QQAndroidBot
 import net.mamoe.mirai.internal.network.Packet
+import net.mamoe.mirai.internal.network.components.NoticeProcessorPipeline.Companion.noticeProcessorPipeline
 import net.mamoe.mirai.internal.network.components.PacketCodec
 import net.mamoe.mirai.internal.network.protocol.packet.chat.*
 import net.mamoe.mirai.internal.network.protocol.packet.chat.image.ImgStore
@@ -27,7 +28,9 @@ import net.mamoe.mirai.internal.network.protocol.packet.login.Heartbeat
 import net.mamoe.mirai.internal.network.protocol.packet.login.StatSvc
 import net.mamoe.mirai.internal.network.protocol.packet.login.WtLogin
 import net.mamoe.mirai.internal.network.protocol.packet.summarycard.SummaryCard
+import net.mamoe.mirai.internal.network.toPacket
 import net.mamoe.mirai.utils.MiraiLoggerWithSwitch
+import net.mamoe.mirai.utils.TypeSafeMap
 
 internal sealed class PacketFactory<TPacket : Packet?> {
     /**
@@ -36,6 +39,17 @@ internal sealed class PacketFactory<TPacket : Packet?> {
     abstract val receivingCommandName: String
 
     open val canBeCached: Boolean get() = true
+
+    protected companion object {
+
+        @JvmStatic
+        suspend fun QQAndroidBot.processPacketThroughPipeline(
+            data: Any?,
+            attributes: TypeSafeMap = TypeSafeMap(),
+        ): Packet {
+            return components.noticeProcessorPipeline.process(this, data, attributes).toPacket()
+        }
+    }
 }
 
 /**
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
index fc5f801a8..f821273e1 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.PbSendMsg.kt
@@ -264,7 +264,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
             sequenceIds = sequenceIds,
             randIds = randIds,
             sequenceIdsInitializer = { size ->
-                IntArray(size) { client.nextFriendSeq() }
+                IntArray(size) { client.sendFriendMessageSeq.next() }
             },
             postInit = {
                 sourceCallback(
@@ -278,7 +278,7 @@ internal object MessageSvcPbSendMsg : OutgoingPacketFactory<MessageSvcPbSendMsg.
                     )
                 )
             },
-            doFragmented = fragmented
+            doFragmented = fragmented,
         )
     }
     /*= buildOutgoingUniPacket(client) {
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt
index 5e76e1ced..79e8e4be1 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/MessageSvc.RequestPushStatus.kt
@@ -9,84 +9,18 @@
 
 package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
 
-import kotlinx.coroutines.CancellationException
-import kotlinx.coroutines.cancel
-import kotlinx.coroutines.delay
-import kotlinx.coroutines.sync.withLock
 import kotlinx.io.core.ByteReadPacket
-import net.mamoe.mirai.Mirai
-import net.mamoe.mirai.contact.ClientKind
-import net.mamoe.mirai.contact.OtherClientInfo
-import net.mamoe.mirai.contact.Platform
-import net.mamoe.mirai.event.events.OtherClientOfflineEvent
-import net.mamoe.mirai.event.events.OtherClientOnlineEvent
 import net.mamoe.mirai.internal.QQAndroidBot
-import net.mamoe.mirai.internal.contact.appId
-import net.mamoe.mirai.internal.contact.createOtherClient
-import net.mamoe.mirai.internal.message.contextualBugReportException
 import net.mamoe.mirai.internal.network.Packet
-import net.mamoe.mirai.internal.network.components.ContactUpdater
-import net.mamoe.mirai.internal.network.handler.logger
 import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPushStatus
 import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
-import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.internal.utils.io.serialization.readUniPacket
 
-internal object MessageSvcRequestPushStatus : IncomingPacketFactory<Packet?>(
-    "MessageSvc.RequestPushStatus", ""
+internal object MessageSvcRequestPushStatus : IncomingPacketFactory<Packet>(
+    "MessageSvc.RequestPushStatus", "",
 ) {
-    override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet? {
+    override suspend fun ByteReadPacket.decode(bot: QQAndroidBot, sequenceId: Int): Packet {
         val packet = readUniPacket(RequestPushStatus.serializer())
-        bot.components[ContactUpdater].otherClientsLock.withLock {
-            val instanceInfo = packet.vecInstanceList?.firstOrNull()
-            val appId = instanceInfo?.iAppId ?: 1
-            return when (packet.status.toInt()) {
-                1 -> { // online
-                    if (bot.otherClients.any { appId == it.appId }) return null
-
-                    suspend fun tryFindInQuery(): OtherClientInfo? {
-                        return Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
-                            ?: kotlin.run {
-                                delay(2000) // sometimes server sync slow
-                                Mirai.getOnlineOtherClientsList(bot).find { it.appId == appId }
-                            }
-                    }
-
-                    val info =
-                        tryFindInQuery() ?: kotlin.run {
-                            bot.network.logger.warning(
-                                contextualBugReportException(
-                                    "SvcRequestPushStatus (OtherClient online)",
-                                    "packet: \n" + packet._miraiContentToString() +
-                                            "\n\nquery: \n" +
-                                            Mirai.getOnlineOtherClientsList(bot)._miraiContentToString(),
-                                    additional = "Failed to find corresponding instanceInfo."
-                                )
-                            )
-                            OtherClientInfo(appId, Platform.WINDOWS, "", "电脑")
-                        }
-
-                    val client = bot.createOtherClient(info)
-                    bot.otherClients.delegate.add(client)
-                    OtherClientOnlineEvent(
-                        client,
-                        ClientKind[packet.nClientType?.toInt() ?: 0]
-                    )
-                }
-
-                2 -> { // off
-                    val client = bot.otherClients.find { it.appId == appId } ?: return null
-                    client.cancel(CancellationException("Offline"))
-                    bot.otherClients.delegate.remove(client)
-                    OtherClientOfflineEvent(client)
-                }
-
-                else -> throw contextualBugReportException(
-                    "decode SvcRequestPushStatus (PC Client status change)",
-                    packet._miraiContentToString(),
-                    additional = "unknown status=${packet.status}"
-                )
-            }
-        }
+        return bot.processPacketThroughPipeline(packet)
     }
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
index e925de0aa..6b44c0de2 100644
--- a/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
+++ b/mirai-core/src/commonMain/kotlin/network/protocol/packet/chat/receive/OnlinePush.ReqPush.kt
@@ -17,15 +17,15 @@ package net.mamoe.mirai.internal.network.protocol.packet.chat.receive
 import kotlinx.io.core.*
 import kotlinx.serialization.Serializable
 import kotlinx.serialization.protobuf.ProtoNumber
-import net.mamoe.mirai.Mirai
 import net.mamoe.mirai.contact.Member
 import net.mamoe.mirai.contact.NormalMember
 import net.mamoe.mirai.contact.User
 import net.mamoe.mirai.data.GroupHonorType
 import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.internal.QQAndroidBot
-import net.mamoe.mirai.internal.contact.*
-import net.mamoe.mirai.internal.contact.info.FriendInfoImpl
+import net.mamoe.mirai.internal.contact.GroupImpl
+import net.mamoe.mirai.internal.contact.checkIsGroupImpl
+import net.mamoe.mirai.internal.contact.checkIsMemberImpl
 import net.mamoe.mirai.internal.network.MultiPacketImpl
 import net.mamoe.mirai.internal.network.Packet
 import net.mamoe.mirai.internal.network.QQAndroidClient
@@ -34,17 +34,12 @@ import net.mamoe.mirai.internal.network.protocol.data.jce.MsgInfo
 import net.mamoe.mirai.internal.network.protocol.data.jce.MsgType0x210
 import net.mamoe.mirai.internal.network.protocol.data.jce.OnlinePushPack
 import net.mamoe.mirai.internal.network.protocol.data.jce.RequestPacket
-import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x115
 import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x122
 import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x27.SubMsgType0x27.*
-import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0x44.Submsgtype0x44
-import net.mamoe.mirai.internal.network.protocol.data.proto.Submsgtype0xb3
 import net.mamoe.mirai.internal.network.protocol.data.proto.TroopTips0x857
 import net.mamoe.mirai.internal.network.protocol.packet.IncomingPacketFactory
 import net.mamoe.mirai.internal.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.internal.network.protocol.packet.buildResponseUniPacket
-import net.mamoe.mirai.internal.network.protocol.packet.list.FriendList
-import net.mamoe.mirai.internal.network.protocol.packet.sendAndExpect
 import net.mamoe.mirai.internal.utils._miraiContentToString
 import net.mamoe.mirai.internal.utils.io.ProtoBuf
 import net.mamoe.mirai.internal.utils.io.serialization.*
@@ -538,20 +533,7 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
     0x111L to ignoredLambda528,
     // 新好友
     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 = Mirai.newFriend(
-            bot, FriendInfoImpl(
-                uin = body.msgAddFrdNotify.fuin,
-                nick = body.msgAddFrdNotify.fuinNick,
-                remark = "",
-            )
-        ).checkIsFriendImpl()
-        bot.friends.delegate.add(new)
-        return@lambda528 bot.getStranger(new.id)?.let {
-            bot.strangers.remove(new.id)
-            sequenceOf(StrangerRelationChangeEvent.Friended(it, new), FriendAddEvent(new))
-        } ?: sequenceOf(FriendAddEvent(new))
+        TODO("removed")
     },
     0xE2L to lambda528 { _ ->
         // TODO: unknown. maybe messages.
@@ -561,35 +543,7 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
         return@lambda528 emptySequence()
     },
     0x44L to lambda528 { bot ->
-        val msg = vProtobuf.loadAs(Submsgtype0x44.MsgBody.serializer())
-
-        val packetList = mutableListOf<Packet>()
-        if (msg.msgFriendMsgSync != null) {
-            when (msg.msgFriendMsgSync.processtype) {
-                3, 9, 10 -> {
-                    if (bot.getFriend(msg.msgFriendMsgSync.fuin) == null) {
-                        val response: FriendList.GetFriendGroupList.Response =
-                            FriendList.GetFriendGroupList.forSingleFriend(
-                                bot.client,
-                                msg.msgFriendMsgSync.fuin
-                            ).sendAndExpect(bot)
-                        response.friendList.firstOrNull()?.let {
-                            val friend = Mirai.newFriend(bot, it.toMiraiFriendInfo()).checkIsFriendImpl()
-                            bot.friends.delegate.add(friend)
-                            packetList.add(FriendAddEvent(friend))
-                        }
-                    }
-                }
-            }
-        }
-        if (msg.msgGroupMsgSync != null) {
-            when (msg.msgGroupMsgSync.msgType) {
-                1, 2 -> {
-                    TODO("removed")
-                }
-            }
-        }
-        return@lambda528 packetList.asSequence()
+        TODO("removed")
     },
     // bot 在其他客户端被踢或主动退出而同步情况
     0xD4L to lambda528 { _ ->
@@ -666,39 +620,10 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
     },
     //好友输入状态
     0x115L to lambda528 { bot ->
-        val body = vProtobuf.loadAs(Submsgtype0x115.SubMsgType0x115.MsgBody.serializer())
-        val friend = bot.getFriend(body.fromUin)
-        val item = body.msgNotifyItem
-        return@lambda528 if (friend != null && item != null) {
-            sequenceOf(FriendInputStatusChangedEvent(friend, item.eventType == 1))
-        } else {
-            emptySequence()
-        }
+        TODO("removed")
     },
     // 群相关,  ModFriendRemark, DelFriend, ModGroupProfile
     0x27L to lambda528 { bot ->
-        fun ModFriendRemark.transform(bot: QQAndroidBot): Sequence<Packet> {
-            return this.msgFrdRmk.asSequence().mapNotNull {
-                val friend = bot.getFriend(it.fuin) ?: return@mapNotNull null
-                val old: String
-                friend.checkIsFriendImpl().friendInfo
-                    .also { info -> old = info.remark }
-                    .remark = it.rmkName
-                // TODO: 2020/4/10 ADD REMARK QUERY
-                FriendRemarkChangeEvent(friend, old, it.rmkName)
-            }
-        }
-
-        fun DelFriend.transform(bot: QQAndroidBot): Sequence<Packet> {
-            return this.uint64Uins.asSequence().mapNotNull {
-
-                val friend = bot.getFriend(it) ?: return@mapNotNull null
-                if (bot.friends.delegate.remove(friend)) {
-                    FriendDeleteEvent(friend)
-                } else null
-            }
-        }
-
         fun ModGroupProfile.transform(bot: QQAndroidBot): Sequence<Packet> {
             return this.msgGroupProfileInfos.asSequence().mapNotNull { info ->
                 when (info.field) {
@@ -729,23 +654,23 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
                            var85.troopface = var3;
                            var85.hasSetNewTroopHead = true;
                          */
-//                        bot.logger.debug(
-//                            contextualBugReportException(
-//                                "解析 Transformers528 0x27L ModGroupProfile 群头像修改",
-//                                forDebug = "this=${this._miraiContentToString()}"
-//                            )
-//                        )
+                        //                        bot.logger.debug(
+                        //                            contextualBugReportException(
+                        //                                "解析 Transformers528 0x27L ModGroupProfile 群头像修改",
+                        //                                forDebug = "this=${this._miraiContentToString()}"
+                        //                            )
+                        //                        )
                         null
                     }
                     3 -> { // troop.credit.data
                         // top_package/akkz.java:3475
                         // top_package/akkz.java:3498
-//                        bot.logger.debug(
-//                            contextualBugReportException(
-//                                "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data",
-//                                forDebug = "this=${this._miraiContentToString()}"
-//                            )
-//                        )
+                        //                        bot.logger.debug(
+                        //                            contextualBugReportException(
+                        //                                "解析 Transformers528 0x27L ModGroupProfile 群 troop.credit.data",
+                        //                                forDebug = "this=${this._miraiContentToString()}"
+                        //                            )
+                        //                        )
                         null
                     }
 
@@ -789,64 +714,15 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
             }
         }
 
-        fun ModCustomFace.transform(bot: QQAndroidBot): Sequence<Packet> {
-            if (uin == bot.id) {
-                return sequenceOf(BotAvatarChangedEvent(bot))
-            }
-            val friend = bot.getFriend(uin) ?: return emptySequence()
-            return sequenceOf(FriendAvatarChangedEvent(friend))
-        }
-
-        fun ModProfile.transform(bot: QQAndroidBot): Sequence<Packet> = buildList<Packet> {
-            var containsUnknown = false
-            msgProfileInfos.forEach { modified ->
-                when (modified.field) {
-                    20002 -> { // 昵称修改
-                        val value = modified.value
-                        val to = value.encodeToString()
-                        if (uin == bot.id) {
-                            val from = bot.nick
-                            if (from != to) {
-                                bot.nick = to
-                                bot.asFriend.checkIsFriendImpl().nick = to
-                                add(BotNickChangedEvent(bot, from, to))
-                            }
-                        } else {
-                            val friend = (bot.getFriend(uin) ?: return@forEach) as FriendImpl
-                            val info = friend.friendInfo
-                            val from = info.nick
-                            when (info) {
-                                is FriendInfoImpl -> info.nick = to
-                                else -> {
-                                    bot.network.logger.debug {
-                                        "Unknown how to update nick for $info"
-                                    }
-                                }
-                            }
-                            add(FriendNickChangedEvent(friend, from, to))
-                        }
-                    }
-                    else -> {
-                        containsUnknown = true
-                    }
-                }
-            }
-            if (msgProfileInfos.isEmpty() || containsUnknown) {
-                bot.network.logger.debug {
-                    "Transformers528 0x27L: new data: ${_miraiContentToString()}"
-                }
-            }
-        }.asSequence()
-
         return@lambda528 vProtobuf.loadAs(SubMsgType0x27MsgBody.serializer()).msgModInfos.asSequence()
             .flatMap {
                 when {
-                    it.msgModFriendRemark != null -> it.msgModFriendRemark.transform(bot)
-                    it.msgDelFriend != null -> it.msgDelFriend.transform(bot)
+                    it.msgModFriendRemark != null -> TODO("removed")
+                    it.msgDelFriend != null -> TODO("removed")
                     it.msgModGroupProfile != null -> it.msgModGroupProfile.transform(bot)
                     it.msgModGroupMemberProfile != null -> it.msgModGroupMemberProfile.transform(bot)
-                    it.msgModCustomFace != null -> it.msgModCustomFace.transform(bot)
-                    it.msgModProfile != null -> it.msgModProfile.transform(bot)
+                    it.msgModCustomFace != null -> TODO("removed")
+                    it.msgModProfile != null -> TODO("removed")
                     else -> {
                         bot.network.logger.debug {
                             "Transformers528 0x27L: new data: ${it._miraiContentToString()}"
@@ -856,5 +732,5 @@ internal object Transformers528 : Map<Long, Lambda528> by mapOf(
                 }
             }
         // 0A 1C 10 28 4A 18 0A 16 08 00 10 A2 FF 8C F0 03 1A 0C E6 BD 9C E6 B1 9F E7 BE A4 E5 8F 8B
-    }
+    },
 )
diff --git a/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt b/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt
new file mode 100644
index 000000000..e29aff6b5
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/utils/AtomicIntSeq.kt
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2019-2021 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.internal.utils
+
+import kotlinx.atomicfu.atomic
+import kotlinx.atomicfu.update
+import net.mamoe.mirai.utils.getRandomUnsignedInt
+import net.mamoe.mirai.utils.toLongUnsigned
+
+// We probably can reduce duplicates by using value classes, but atomicFU compiler might not be able to compile it.
+
+// TODO: 2021/6/27 tests
+internal class AtomicIntSeq private constructor(
+    initial: Int,
+    private val maxExclusive: Int,
+) {
+    private val value = atomic(initial)
+
+    /**
+     * Increment [value] within the range from 0 (inclusive) to [maxExclusive] (exclusive).
+     */
+    fun next(): Int = value.incrementAndGet().mod(maxExclusive) // positive
+
+    /**
+     * Atomically update [value] if it is smaller than [new].
+     */
+    fun updateIfSmallerThan(new: Int): Boolean {
+        value.update { instant ->
+            if (instant < new) new else return false
+        }
+        return true
+    }
+
+    fun updateIfDifferentWith(new: Int): Boolean {
+        value.update { instant ->
+            if (instant == new) return false
+            new
+        }
+        return true
+    }
+
+    companion object {
+        @JvmStatic
+        fun forMessageSeq() = AtomicIntSeq(0, Int.MAX_VALUE)
+
+        @JvmStatic
+        fun forPrivateSync() = AtomicIntSeq(getRandomUnsignedInt(), 65535)
+    }
+}
+
+// TODO: 2021/6/27 tests
+internal class AtomicLongSeq(
+    initial: Long = getRandomUnsignedInt().toLongUnsigned(),
+    private val maxExclusive: Long = 65535,
+) {
+    private val value = atomic(initial)
+
+    /**
+     * Increment [value] within the range from 0 (inclusive) to [maxExclusive] (exclusive).
+     */
+    fun next(): Long = value.incrementAndGet().mod(maxExclusive) // positive
+
+    /**
+     * Atomically update [value] if it is smaller than [new].
+     */
+    fun updateIfSmallerThan(new: Long) {
+        value.update { instant ->
+            if (instant < new) new else return
+        }
+    }
+}
\ No newline at end of file