diff --git a/README.md b/README.md
index 7df29189a..eaa3eee21 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,4 @@
 # Mirai
-[![Codacy Badge](https://api.codacy.com/project/badge/Grade/7d0ec3ea244b424f93a6f59038a9deeb)](https://www.codacy.com/manual/Him188/mirai?utm_source=github.com&utm_medium=referral&utm_content=mamoe/mirai&utm_campaign=Badge_Grade)
 [![Gitter](https://badges.gitter.im/mamoe/mirai.svg)](https://gitter.im/mamoe/mirai?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
 [![Actions Status](https://github.com/mamoe/mirai/workflows/CI/badge.svg)](https://github.com/mamoe/mirai/actions)
 [![Download](https://api.bintray.com/packages/him188moe/mirai/mirai-core/images/download.svg)](https://bintray.com/him188moe/mirai/mirai-core/)  
diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md
index 45b54c36c..5d583d6f9 100644
--- a/mirai-api-http/README_CH.md
+++ b/mirai-api-http/README_CH.md
@@ -1,48 +1,108 @@
 # mirai-api-http
 
-<b>
-Mirai-API-http 提供HTTP API供所有语言使用mirai<br>
-</b>
+<b>Mirai-API-http 提供HTTP API供所有语言使用mirai</b>
+
+### 快速开始
+
+```kotlin
+fun main() {
+    val bot = Bot(123456789, "password")
+
+    bot.login()
+
+    MiraiHttpAPIServer.start()
+    
+    bot.network.awaitDisconnection()
+}
+```
 
 ### 开始会话-认证(Authorize)
 
 ```
 [POST] /auth
 ```
-使用此方法验证你的会话连接, 并将这个会话绑定一个BOT<br>
-注意: 每个会话只能绑定一个BOT.
+使用此方法验证你的身份,并返回一个会话
 
-#### 请求:<br>
+#### 请求:
+
+```json5
+{
+    "authKey": "U9HSaDXl39ksd918273hU"
+}
+```
 
 |  名字    | 类型 | 可选 | 举例 | 说明 |
 | --- | --- | --- | --- | --- |
-| key  |  String |false|U9HSaDXl39ksd918273hU|MIRAI API HTTP key, HTTP API的核心key|
-| qq   |  String |false|1040400290|需要绑定的BOT QQ号|
+| authKey |  String |false|"U9HSaDXl39ksd918273hU"|创建Mirai-Http-Server时生成的key,可在启动时指定或随机生成|
 
+#### 响应: 返回(成功):
 
-#### 返回(成功):<br>
+```json5
+{
+    "code": 0,
+    "session": "UnVerifiedSession"
+}
+```
 
 |  名字    | 类型 | 举例 | 说明|
 | --- | --- | ---  | --- |
-| code |Int |0|返回状态|
-| session |String |UANSHDKSLAOISN|你的session key|
+| code |Int |0|返回状态码|
+| session |String |"UnVerifiedSession"|你的session key|
 
-#### 状态码:<br>
+#### 状态码:
 
 |  代码    | 原因|
 | --- | --- |
 | 0 | 正常 |
-| 1 | 错误的MIRAI API HTTP key|
-| 2 | 试图绑定不存在的bot|
+| 1 | 错误的MIRAI API HTTP auth key|
 
  session key 是使用以下方法必须携带的</br>
- session key 需要被以cookie的形式上报 <b>cookies</b> :
+ session key 使用前必须进行校验和绑定指定的Bot,**每个Session只能绑定一个Bot,但一个Bot可有多个Session**
 
-|  名字  | 值 |
-| --- | --- |
-| session |your session key here |
 
-如果出现HTTP 403错误码,代表session key已过期, 需要重新获取
+
+### 校验Session
+
+```
+[post] /verify
+```
+
+使用此方法校验并激活你的Session,同时将Session与一个**已登录**的Bot绑定
+
+#### 请求:
+
+```json5
+{
+    "sessionKey": "UnVerifiedSession",
+    "qq": 123456789
+}
+```
+
+| 名字       | 类型   | 可选  | 举例                | 说明                       |
+| ---------- | ------ | ----- | ------------------- | -------------------------- |
+| sessionKey | String | false | "UnVerifiedSession" | 你的session key            |
+| qq         | Long   | false | 123456789           | Session将要绑定的Bot的qq号 |
+
+#### 响应: 返回统一状态码(后续不再赘述)
+
+```json5
+{
+    "code": 0,
+    "msg": "success"
+}
+```
+
+| 状态码 | 原因                               |
+| ------ | ---------------------------------- |
+| 0      | 正常                               |
+| 1      | 错误的auth key                     |
+| 2      | 绑定的Bot不存在                    |
+| 3      | Session失效或不存在                |
+| 4      | Session未认证(未激活)              |
+| 5      | 发送消息目标不存在(指定对象不存在) |
+| 400    | 错误的访问,如参数错误等           |
+
+ 
 
 ### 发送好友消息
 
@@ -50,3 +110,198 @@ Mirai-API-http 提供HTTP API供所有语言使用mirai<br>
 [POST] /sendFriendMessage
 ```
 
+使用此方法向指定好友发送消息
+
+#### 请求
+
+```json5
+{
+    "sessionKey": "YourSession",
+    "target": 987654321,
+    "messageChain": [
+        { "type": "Plain", "text":"hello\n" },
+        { "type": "Plain", "text":"world" }
+    ]
+}
+```
+
+| 名字         | 类型   | 可选  | 举例        | 说明                             |
+| ------------ | ------ | ----- | ----------- | -------------------------------- |
+| sessionKey   | String | false | YourSession | 已经激活的Session                |
+| target       | Long   | false | 987654321   | 发送消息目标好友的QQ号           |
+| messageChain | Array  | false | []          | 消息链,是一个消息对象构成的数组 |
+
+#### 响应: 返回统一状态码
+
+```json5
+{
+    "code": 0,
+    "msg": "success"
+}
+```
+
+
+
+### 发送群消息
+
+```
+[POST] /sendGroupMessage
+```
+
+使用此方法向指定群发送消息
+
+#### 请求
+
+```json5
+{
+    "sessionKey": "YourSession",
+    "target": 987654321,
+    "messageChain": [
+        { "type": "Plain", "text":"hello\n" },
+        { "type": "Plain", "text":"world" }
+    ]
+}
+```
+
+| 名字         | 类型   | 可选  | 举例        | 说明                             |
+| ------------ | ------ | ----- | ----------- | -------------------------------- |
+| sessionKey   | String | false | YourSession | 已经激活的Session                |
+| target       | Long   | false | 987654321   | 发送消息目标群的群号             |
+| messageChain | Array  | false | []          | 消息链,是一个消息对象构成的数组 |
+
+#### 响应: 返回统一状态码
+
+```json5
+{
+    "code": 0,
+    "msg": "success"
+}
+```
+
+
+
+### 获取Bot收到的消息
+
+```
+[GET] /fetchMessage?sessionKey=YourSessionKey&count=10
+```
+
+#### 请求:
+
+| 名字       | 可选  | 举例           | 说明            |
+| ---------- | ----- | -------------- | --------------- |
+| sessionKey | false | YourSessionKey | 你的session key |
+| count      | false | 10             | 获取消息的数量  |
+
+#### 响应: 返回JSON对象
+
+```json5
+[{
+    "type": "GroupMessage",        // 消息类型:GroupMessage或FriendMessage
+	"messageChain": [{             // 消息链,是一个消息对象构成的数组
+        "type": "Plain",
+        "text": "Miral牛逼"
+    }],
+    "sender": {                      // 发送者信息
+        "id": 123456789,             // 发送者的QQ号码
+        "memberName": "化腾",        // 发送者的群名片
+        "permission": "MEMBER",      // 发送者的群限权:OWNER、ADMINISTRATOR或MEMBER
+        "group": {                   // 消息发送群的信息
+            "id": 1234567890,        // 发送群的群号
+            "name": "Miral Technology" // 发送群的群名称
+        }
+    }
+ },
+ {
+    "type": "FriendMessage",         // 消息类型:GroupMessage或FriendMessage
+        "messageChain": [{           // 消息链,是一个消息对象构成的数组
+        "type": "Plain",
+        "text": "Miral牛逼"
+    }],
+    "sender": {                      // 发送者信息
+        "id": 1234567890,            // 发送者的QQ号码
+        "nickName": "",              // 发送者的昵称
+        "remark": ""                 // 发送者的备注
+    }
+}]
+```
+
+
+
+### 消息类型一览
+
+#### 消息是构成消息链的基本对象,目前支持的消息类型有
+
++ [x] At,@消息
++ [x] Face,表情消息
++ [x] Plain,文字消息
++ [ ] Image,图片消息
++ [ ] Xml,Xml卡片消息
++ [ ] 敬请期待
+
+#### At
+
+```json5
+{
+    "type": "At",
+    "target": 123456,
+    "display": "@Mirai"
+}
+```
+
+| 名字    | 类型   | 说明                      |
+| ------- | ------ | ------------------------- |
+| target  | Long   | 群员QQ号                  |
+| display | String | @时显示的文本如:"@Mirai" |
+
+#### Face
+
+```json5
+{
+    "type": "Face",
+    "faceID": 123
+}
+```
+
+| 名字   | 类型 | 说明       |
+| ------ | ---- | ---------- |
+| faceID | Int  | QQ表情编号 |
+
+#### Plain
+
+```json5
+{
+    "type": "Plain",
+    "text": "Mirai牛逼"
+}
+```
+
+| 名字 | 类型   | 说明     |
+| ---- | ------ | -------- |
+| text | String | 文字消息 |
+
+#### Image
+
+```json5
+{
+    "type": "Image"
+    // 暂时不支持Image
+}
+```
+
+| 名字 | 类型 | 说明 |
+| ---- | ---- | ---- |
+|      |      |      |
+
+#### Xml
+
+```json5
+{
+    "type": "Xml",
+    "xml": "XML"
+}
+```
+
+| 名字 | 类型   | 说明    |
+| ---- | ------ | ------- |
+| xml  | String | XML文本 |
\ No newline at end of file
diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt
index 2d4664b2f..651f3ada2 100644
--- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt
+++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/MiraiHttpAPIServer.kt
@@ -18,16 +18,11 @@ object MiraiHttpAPIServer {
     @UseExperimental(KtorExperimentalAPI::class)
     fun start(
         port: Int = 8080,
-        authKey: String? = null,
+        authKey: String,
         callback: (() -> Unit)? = null
     ) {
-        authKey?.apply {
-            if (authKey.length in 8..128) {
-                SessionManager.authKey = authKey
-            } else {
-                logger.error("Expected authKey length is between 8 to 128")
-            }
-        }
+        require(authKey.length in 8..128) { "Expected authKey length is between 8 to 128" }
+        SessionManager.authKey = authKey
 
         // TODO: start是无阻塞的,理应获取启动状态后再执行后续代码
         try {
diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt
index 6ef8bb789..2194a537a 100644
--- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt
+++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/Session.kt
@@ -9,7 +9,7 @@ import net.mamoe.mirai.message.MessagePacket
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
-tailrec fun generateSessionKey():String{
+tailrec fun generateSessionKey(): String {
     fun generateRandomSessionKey(): String {
         val all = "QWERTYUIOPASDFGHJKLZXCVBNM1234567890qwertyuiopasdfghjklzxcvbnm"
         return buildString(capacity = 8) {
@@ -20,27 +20,27 @@ tailrec fun generateSessionKey():String{
     }
 
     val key = generateRandomSessionKey()
-    if(!SessionManager.allSession.containsKey(key)){
+    if (!SessionManager.allSession.containsKey(key)) {
         return key
     }
 
     return generateSessionKey()
 }
 
-object SessionManager {
+internal object SessionManager {
 
-    val allSession:MutableMap<String,Session> = mutableMapOf()
+    val allSession: MutableMap<String, Session> = mutableMapOf()
 
-    lateinit var authKey:String
+    lateinit var authKey: String
 
 
-    fun createTempSession():TempSession = TempSession(EmptyCoroutineContext).also {newTempSession ->
+    fun createTempSession(): TempSession = TempSession(EmptyCoroutineContext).also { newTempSession ->
         allSession[newTempSession.key] = newTempSession
         //设置180000ms后检测并回收
-        newTempSession.launch{
+        newTempSession.launch {
             delay(180000)
             allSession[newTempSession.key]?.run {
-                if(this is TempSession)
+                if (this is TempSession)
                     closeSession(newTempSession.key)
             }
         }
@@ -50,15 +50,13 @@ object SessionManager {
 
     fun containSession(sessionKey: String): Boolean = allSession.containsKey(sessionKey)
 
-    fun closeSession(sessionKey: String) = allSession.remove(sessionKey)?.also {it.close() }
+    fun closeSession(sessionKey: String) = allSession.remove(sessionKey)?.also { it.close() }
 
     fun closeSession(session: Session) = closeSession(session.key)
 
 }
 
 
-
-
 /**
  * @author NaturalHG
  * 这个用于管理不同Client与Mirai HTTP的会话
@@ -68,20 +66,19 @@ object SessionManager {
  */
 abstract class Session internal constructor(
     coroutineContext: CoroutineContext
-): CoroutineScope {
+) : CoroutineScope {
     val supervisorJob = SupervisorJob(coroutineContext[Job])
     final override val coroutineContext: CoroutineContext = supervisorJob + coroutineContext
 
-    val key:String = generateSessionKey()
+    val key: String = generateSessionKey()
 
 
-    internal open fun close(){
+    internal open fun close() {
         supervisorJob.complete()
     }
 }
 
 
-
 /**
  * 任何新链接建立后分配一个[TempSession]
  *
@@ -93,10 +90,10 @@ class TempSession internal constructor(coroutineContext: CoroutineContext) : Ses
  * 任何[TempSession]认证后转化为一个[AuthedSession]
  * 在这一步[AuthedSession]应该已经有assigned的bot
  */
-class AuthedSession internal constructor(val bot: Bot, coroutineContext: CoroutineContext):Session(coroutineContext){
+class AuthedSession internal constructor(val bot: Bot, coroutineContext: CoroutineContext) : Session(coroutineContext) {
 
     val messageQueue = MessageQueue()
-    private val _listener : Listener<MessagePacket<*, *>>
+    private val _listener: Listener<MessagePacket<*, *>>
 
     init {
         bot.subscribeMessages {
diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt
index c3d254785..5550ad079 100644
--- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt
+++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/dto/ContactDTO.kt
@@ -18,7 +18,8 @@ data class QQDTO(
     val remark: String
 ) : ContactDTO()
 
-suspend fun QQDTO(qq: QQ): QQDTO = QQDTO(qq.id, qq.queryProfile().nickname, qq.queryRemark().value)
+// TODO: queryProfile.nickname & queryRemark.value not support now
+suspend fun QQDTO(qq: QQ): QQDTO = QQDTO(qq.id, "", "")
 
 @Serializable
 data class MemberDTO(
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
index 1aaee6040..db5604f6a 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/ContactImpl.kt
@@ -59,6 +59,9 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
         TODO("not implemented")
     }
 
+    override fun equals(other: Any?): Boolean {
+        return other is QQ && other.id == this.id
+    }
 }
 
 
@@ -107,6 +110,9 @@ internal class MemberImpl(
         return mute(0)
     }
 
+    override fun equals(other: Any?): Boolean {
+        return other is Member && other.id == this.id
+    }
 }
 
 
@@ -324,4 +330,8 @@ internal class GroupImpl(
             }
         }
     }
+
+    override fun equals(other: Any?): Boolean {
+        return other is Group && other.id == this.id
+    }
 }
\ No newline at end of file
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/OIDB.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/OIDB.kt
index 6e4e20863..a8b39c438 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/OIDB.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/data/proto/OIDB.kt
@@ -16,7 +16,7 @@ class Oidb0x88d : ProtoBuf {
     @Serializable
     class ReqGroupInfo(
         @SerialId(1) val groupCode: Long = 0L,
-        @SerialId(2) val stgroupinfo: Oidb0x88d.GroupInfo? = null,
+        @SerialId(2) val stgroupinfo: GroupInfo? = null,
         @SerialId(3) val lastGetGroupNameTime: Int = 0
     ) : ProtoBuf
 
@@ -24,7 +24,7 @@ class Oidb0x88d : ProtoBuf {
     class RspGroupInfo(
         @SerialId(1) val groupCode: Long = 0L,
         @SerialId(2) val result: Int = 0,
-        @SerialId(3) val stgroupinfo: Oidb0x88d.GroupInfo? = null
+        @SerialId(3) val stgroupinfo: GroupInfo? = null
     ) : ProtoBuf
 
     @Serializable
@@ -165,24 +165,24 @@ class Oidb0x88d : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val stzrspgroupinfo: List<Oidb0x88d.RspGroupInfo>? = null,
+        @SerialId(1) val stzrspgroupinfo: List<RspGroupInfo>? = null,
         @SerialId(2) val errorinfo: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
         @SerialId(1) val appid: Int = 0,
-        @SerialId(2) val stzreqgroupinfo: List<Oidb0x88d.ReqGroupInfo>? = null,
+        @SerialId(2) val stzreqgroupinfo: List<ReqGroupInfo>? = null,
         @SerialId(3) val pcClientVersion: Int = 0
     ) : ProtoBuf
 
     @Serializable
     class GroupHeadPortrait(
         @SerialId(1) val picCnt: Int = 0,
-        @SerialId(2) val msgInfo: List<Oidb0x88d.GroupHeadPortraitInfo>? = null,
+        @SerialId(2) val msgInfo: List<GroupHeadPortraitInfo>? = null,
         @SerialId(3) val defaultId: Int = 0,
         @SerialId(4) val verifyingPicCnt: Int = 0,
-        @SerialId(5) val msgVerifyingpicInfo: List<Oidb0x88d.GroupHeadPortraitInfo>? = null
+        @SerialId(5) val msgVerifyingpicInfo: List<GroupHeadPortraitInfo>? = null
     ) : ProtoBuf
 }
 
@@ -209,18 +209,18 @@ class Oidb0x89a : ProtoBuf {
         @SerialId(11) val certificationType: Int? = null,
         @SerialId(12) val ingCertificationText: ByteArray? = null,
         @SerialId(13) val ingGroupRichFingerMemo: ByteArray? = null,
-        @SerialId(14) val stGroupNewguidelines: Oidb0x89a.GroupNewGuidelinesInfo? = null,
+        @SerialId(14) val stGroupNewguidelines: GroupNewGuidelinesInfo? = null,
         @SerialId(15) val groupFace: Int? = null,
         @SerialId(16) val addOption: Int? = null,
         @SerialId(17) val shutupTime: Int? = null,
         @SerialId(18) val groupTypeFlag: Int? = null,
         @SerialId(19) val stringGroupTag: List<ByteArray>? = null,
-        @SerialId(20) val msgGroupGeoInfo: Oidb0x89a.GroupGeoInfo? = null,
+        @SerialId(20) val msgGroupGeoInfo: GroupGeoInfo? = null,
         @SerialId(21) val groupClassExt: Int? = null,
         @SerialId(22) val ingGroupClassText: ByteArray? = null,
         @SerialId(23) val appPrivilegeFlag: Int? = null,
         @SerialId(24) val appPrivilegeMask: Int? = null,
-        @SerialId(25) val stGroupExInfo: Oidb0x89a.GroupExInfoOnly? = null,
+        @SerialId(25) val stGroupExInfo: GroupExInfoOnly? = null,
         @SerialId(26) val groupSecLevel: Int? = null,
         @SerialId(27) val groupSecLevelInfo: Int? = null,
         @SerialId(28) val subscriptionUin: Long? = null,
@@ -260,7 +260,7 @@ class Oidb0x89a : ProtoBuf {
     @Serializable
     class ReqBody(
         @SerialId(1) val groupCode: Long = 0L,
-        @SerialId(2) val stGroupInfo: Oidb0x89a.Groupinfo? = null,
+        @SerialId(2) val stGroupInfo: Groupinfo? = null,
         @SerialId(3) val originalOperatorUin: Long = 0L,
         @SerialId(4) val reqGroupOpenAppid: Int = 0
     ) : ProtoBuf
@@ -278,13 +278,13 @@ class Cmd0x7cb : ProtoBuf {
     class RspBody(
         @SerialId(1) val timeStamp: Int = 0,
         @SerialId(2) val timeGap: Int = 0,
-        @SerialId(3) val commentConfigs: List<Cmd0x7cb.CommentConfig>? = null,
+        @SerialId(3) val commentConfigs: List<CommentConfig>? = null,
         @SerialId(4) val attendTipsToA: String = "",
         @SerialId(5) val firstMsgTips: String = "",
-        @SerialId(6) val cancleConfig: List<Cmd0x7cb.ConfigItem>? = null,
-        @SerialId(7) val msgDateRequest: Cmd0x7cb.DateRequest? = null,
+        @SerialId(6) val cancleConfig: List<ConfigItem>? = null,
+        @SerialId(7) val msgDateRequest: DateRequest? = null,
         @SerialId(8) val msgHotLocale: List<ByteArray>? = null,//List<AppointDefine.LocaleInfo>
-        @SerialId(9) val msgTopicList: List<Cmd0x7cb.TopicConfig>? = null,
+        @SerialId(9) val msgTopicList: List<TopicConfig>? = null,
         @SerialId(10) val travelMsgTips: String = "",
         @SerialId(11) val travelProfileTips: String = "",
         @SerialId(12) val travelAttenTips: String = "",
@@ -294,7 +294,7 @@ class Cmd0x7cb : ProtoBuf {
     @Serializable
     class CommentConfig(
         @SerialId(1) val appointSubject: Int = 0,
-        @SerialId(2) val msgConfigs: List<Cmd0x7cb.ConfigItem>? = null
+        @SerialId(2) val msgConfigs: List<ConfigItem>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -346,16 +346,16 @@ class GroupAppPb : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val fullList: GroupAppPb.AppList? = null,
-        @SerialId(2) val groupGrayList: GroupAppPb.AppList? = null,
-        @SerialId(3) val redPointList: GroupAppPb.AppList? = null,
+        @SerialId(1) val fullList: AppList? = null,
+        @SerialId(2) val groupGrayList: AppList? = null,
+        @SerialId(3) val redPointList: AppList? = null,
         @SerialId(4) val cacheInterval: Int = 0
     ) : ProtoBuf
 
     @Serializable
     class AppList(
         @SerialId(1) val hash: String = "",
-        @SerialId(2) val infos: List<GroupAppPb.AppInfo>? = null
+        @SerialId(2) val infos: List<AppInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -371,7 +371,7 @@ class GroupAppPb : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val client: GroupAppPb.ClientInfo? = null,
+        @SerialId(1) val client: ClientInfo? = null,
         @SerialId(2) val groupId: Long = 0L,
         @SerialId(3) val groupType: Int = 0,
         @SerialId(4) val fullListHash: String = "",
@@ -427,20 +427,20 @@ class Oidb0xbcb : ProtoBuf {
 
     @Serializable
     class CheckUrlRsp(
-        @SerialId(1) val results: List<Oidb0xbcb.UrlCheckResult>? = null,
+        @SerialId(1) val results: List<UrlCheckResult>? = null,
         @SerialId(2) val nextReqDuration: Int = 0
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
         @SerialId(9) val notUseCache: Int = 0,
-        @SerialId(10) val checkUrlReq: Oidb0xbcb.CheckUrlReq? = null
+        @SerialId(10) val checkUrlReq: CheckUrlReq? = null
     ) : ProtoBuf
 
     @Serializable
     class RspBody(
         @SerialId(1) val wording: String = "",
-        @SerialId(10) val checkUrlRsp: Oidb0xbcb.CheckUrlRsp? = null
+        @SerialId(10) val checkUrlRsp: CheckUrlRsp? = null
     ) : ProtoBuf
 
     @Serializable
@@ -510,7 +510,7 @@ class Oidb0xbe8 : ProtoBuf {
         @SerialId(3) val reqOfPopupFlag: Int = 0,
         @SerialId(4) val rstOfPopupFlag: Int = 0,
         @SerialId(5) val mqq808WelcomepageFlag: Int = 0,
-        @SerialId(6) val msgPopupResult: List<Oidb0xbe8.PopupResult>? = null
+        @SerialId(6) val msgPopupResult: List<PopupResult>? = null
     ) : ProtoBuf
 }
 
@@ -525,8 +525,8 @@ class Cmd0x7de : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val msgHead: Cmd0x7de.BusiRespHead? = null,
-        @SerialId(2) val msgUserList: List<Cmd0x7de.UserProfile>? = null,
+        @SerialId(1) val msgHead: BusiRespHead? = null,
+        @SerialId(2) val msgUserList: List<UserProfile>? = null,
         @SerialId(3) val ended: Int = 0,
         @SerialId(4) val cookie: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
@@ -541,7 +541,7 @@ class Cmd0x7de : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val msgHead: Cmd0x7de.BusiReqHead? = null,
+        @SerialId(1) val msgHead: BusiReqHead? = null,
         @SerialId(2) val msgLbsInfo: AppointDefine.LBSInfo? = null,
         @SerialId(3) val time: Int = 0,
         @SerialId(4) val subject: Int = 0,
@@ -626,7 +626,7 @@ class Oidb0xc35 : ProtoBuf {
     @Serializable
     class ReqBody(
         @SerialId(1) val uin: Long = 0L,
-        @SerialId(2) val msgExposeInfo: List<Oidb0xc35.ExposeItem>? = null
+        @SerialId(2) val msgExposeInfo: List<ExposeItem>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -700,8 +700,8 @@ class Cmd0xccb : ProtoBuf {
         @SerialId(1) val type: Int = 0,
         @SerialId(2) val destUin: Long = 0L,
         @SerialId(3) val groupCode: Long = 0L,
-        @SerialId(4) val c2cMsg: List<Cmd0xccb.C2cMsgInfo>? = null,
-        @SerialId(5) val groupMsg: List<Cmd0xccb.GroupMsgInfo>? = null
+        @SerialId(4) val c2cMsg: List<C2cMsgInfo>? = null,
+        @SerialId(5) val groupMsg: List<GroupMsgInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -709,8 +709,8 @@ class Cmd0xccb : ProtoBuf {
         @SerialId(1) val type: Int = 0,
         @SerialId(2) val destUin: Long = 0L,
         @SerialId(3) val groupCode: Long = 0L,
-        @SerialId(4) val c2cMsg: List<Cmd0xccb.C2cMsgInfo>? = null,
-        @SerialId(5) val groupMsg: List<Cmd0xccb.GroupMsgInfo>? = null,
+        @SerialId(4) val c2cMsg: List<C2cMsgInfo>? = null,
+        @SerialId(5) val groupMsg: List<GroupMsgInfo>? = null,
         @SerialId(6) val resId: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
@@ -786,7 +786,7 @@ class Oidb0x5e1 : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(11) val msgUinData: List<Oidb0x5e1.UdcUinData>? = null,
+        @SerialId(11) val msgUinData: List<UdcUinData>? = null,
         @SerialId(12) val uint64UnfinishedUins: List<Long>? = null
     ) : ProtoBuf
 
@@ -850,18 +850,18 @@ class Oidb0xc90 : ProtoBuf {
 
     @Serializable
     class CommunityWebInfo(
-        @SerialId(1) val communityInfoItem: List<Oidb0xc90.CommunityConfigInfo>? = null,
+        @SerialId(1) val communityInfoItem: List<CommunityConfigInfo>? = null,
         @SerialId(2) val page: Int = 0,
         @SerialId(3) val end: Int = 0
     ) : ProtoBuf
 
     @Serializable
     class RspBody(
-        @SerialId(1) val communityInfoItem: List<Oidb0xc90.CommunityConfigInfo>? = null,
+        @SerialId(1) val communityInfoItem: List<CommunityConfigInfo>? = null,
         @SerialId(2) val jumpConcernCommunityUrl: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(3) val communityTitleWording: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(4) val moreUrlWording: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val webCommunityInfo: Oidb0xc90.CommunityWebInfo? = null,
+        @SerialId(5) val webCommunityInfo: CommunityWebInfo? = null,
         @SerialId(6) val jumpCommunityChannelUrl: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
@@ -889,7 +889,7 @@ class Cmd0xd8a : ProtoBuf {
         @SerialId(1) val uin: Long = 0L,
         @SerialId(2) val cmd: Int = 0,
         @SerialId(3) val body: String = "",
-        @SerialId(4) val clientInfo: Cmd0xd8a.ClientInfo? = null
+        @SerialId(4) val clientInfo: ClientInfo? = null
     ) : ProtoBuf
 
     @Serializable
@@ -903,7 +903,7 @@ class Cmd0xd8a : ProtoBuf {
 class Oidb0xb6f : ProtoBuf {
     @Serializable
     class ReportFreqRspBody(
-        @SerialId(1) val identity: Oidb0xb6f.Identity? = null,
+        @SerialId(1) val identity: Identity? = null,
         @SerialId(4) val remainTimes: Long = 0L,
         @SerialId(5) val expireTime: Int = 0
     ) : ProtoBuf
@@ -928,18 +928,18 @@ class Oidb0xb6f : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val reportFreqRsp: Oidb0xb6f.ReportFreqRspBody? = null
+        @SerialId(1) val reportFreqRsp: ReportFreqRspBody? = null
     ) : ProtoBuf
 
     @Serializable
     class ReportFreqReqBody(
-        @SerialId(1) val identity: Oidb0xb6f.Identity? = null,
+        @SerialId(1) val identity: Identity? = null,
         @SerialId(2) val invokeTimes: Long = 1L
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val reportFreqReq: Oidb0xb6f.ReportFreqReqBody? = null
+        @SerialId(1) val reportFreqReq: ReportFreqReqBody? = null
     ) : ProtoBuf
 }
 
@@ -974,7 +974,7 @@ class Cmd0x7cd : ProtoBuf {
         @SerialId(1) val stamp: Int = 0,
         @SerialId(2) val over: Int = 0,
         @SerialId(3) val next: Int = 0,
-        @SerialId(4) val msgAppointsInfo: List<Cmd0x7cd.AppointBrife>? = null
+        @SerialId(4) val msgAppointsInfo: List<AppointBrife>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1015,7 +1015,7 @@ class Oidb0xc0c : ProtoBuf {
         @SerialId(2) val taskType: Int = 0,
         @SerialId(3) val rowkey: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(4) val feedsId: Long = 0L,
-        @SerialId(5) val msgVideoFromType: Oidb0xc0c.VideoSrcType? = null
+        @SerialId(5) val msgVideoFromType: VideoSrcType? = null
     ) : ProtoBuf
 }
 
@@ -1035,7 +1035,7 @@ class Cmd0x5fb : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val msgHead: Cmd0x5fb.BusiReqHead? = null,
+        @SerialId(1) val msgHead: BusiReqHead? = null,
         @SerialId(2) val msgLbsInfo: AppointDefine.LBSInfo? = null,
         @SerialId(3) val reqInfo: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
@@ -1069,7 +1069,7 @@ class Cmd0x5fb : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val msgHead: Cmd0x5fb.BusiRespHead? = null,
+        @SerialId(1) val msgHead: BusiRespHead? = null,
         @SerialId(2) val msgUserList: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 }
@@ -1097,14 +1097,14 @@ class Oidb0xb61 : ProtoBuf {
     class RspBody(
         @SerialId(1) val wording: String = "",
         @SerialId(2) val nextReqDuration: Int = 0,
-        @SerialId(10) val getAppinfoRsp: Oidb0xb61.GetAppinfoRsp? = null,
-        @SerialId(11) val getMqqappUrlRsp: Oidb0xb61.GetPkgUrlRsp? = null
+        @SerialId(10) val getAppinfoRsp: GetAppinfoRsp? = null,
+        @SerialId(11) val getMqqappUrlRsp: GetPkgUrlRsp? = null
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
-        @SerialId(10) val getAppinfoReq: Oidb0xb61.GetAppinfoReq? = null,
-        @SerialId(11) val getMqqappUrlReq: Oidb0xb61.GetPkgUrlReq? = null
+        @SerialId(10) val getAppinfoReq: GetAppinfoReq? = null,
+        @SerialId(11) val getMqqappUrlReq: GetPkgUrlReq? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1147,8 +1147,8 @@ class Oidb0xb60 : ProtoBuf {
     @Serializable
     class RspBody(
         @SerialId(1) val wording: String = "",
-        @SerialId(10) val getPrivilegeRsp: Oidb0xb60.GetPrivilegeRsp? = null,
-        @SerialId(11) val checkUrlRsp: Oidb0xb60.CheckUrlRsp? = null
+        @SerialId(10) val getPrivilegeRsp: GetPrivilegeRsp? = null,
+        @SerialId(11) val checkUrlRsp: CheckUrlRsp? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1159,9 +1159,9 @@ class Oidb0xb60 : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val clientInfo: Oidb0xb60.ClientInfo? = null,
-        @SerialId(10) val getPrivilegeReq: Oidb0xb60.GetPrivilegeReq? = null,
-        @SerialId(11) val checkUrlReq: Oidb0xb60.CheckUrlReq? = null
+        @SerialId(1) val clientInfo: ClientInfo? = null,
+        @SerialId(10) val getPrivilegeReq: GetPrivilegeReq? = null,
+        @SerialId(11) val checkUrlReq: CheckUrlReq? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1220,10 +1220,10 @@ class Oidb0xc0b : ProtoBuf {
         @SerialId(2) val canGetCoinCount: Int = 0,
         @SerialId(3) val coinIconUrl: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(5) val lastCompletedTaskStamp: Long = 0L,
-        @SerialId(6) val cmsWording: List<Oidb0xc0b.KanDianCMSActivityInfo>? = null,
+        @SerialId(6) val cmsWording: List<KanDianCMSActivityInfo>? = null,
         @SerialId(7) val lastCmsActivityStamp: Long = 0L,
-        @SerialId(8) val msgKandianCoinRemind: Oidb0xc0b.KanDianCoinRemind? = null,
-        @SerialId(9) val msgKandianTaskRemind: Oidb0xc0b.KanDianTaskRemind? = null
+        @SerialId(8) val msgKandianCoinRemind: KanDianCoinRemind? = null,
+        @SerialId(9) val msgKandianTaskRemind: KanDianTaskRemind? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1274,7 +1274,7 @@ class Cmd0xc85 : ProtoBuf {
     class RspBody(
         @SerialId(101) val result: Int = 0,
         @SerialId(102) val recentInteractionTime: Int = 0,
-        @SerialId(103) val interactionDetailInfo: Cmd0xc85.InteractionDetailInfo? = null
+        @SerialId(103) val interactionDetailInfo: InteractionDetailInfo? = null
     ) : ProtoBuf
 }
 
@@ -1306,7 +1306,7 @@ class Cmd0x7ce : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val msgAppointsInfo: List<Cmd0x7ce.AppintDetail>? = null,
+        @SerialId(1) val msgAppointsInfo: List<AppintDetail>? = null,
         @SerialId(2) val secureFlag: Int = 0,
         @SerialId(3) val secureTips: String = ""
     ) : ProtoBuf
@@ -1349,7 +1349,7 @@ class Oidb0xc6c : ProtoBuf {
     @Serializable
     class ReqBody(
         @SerialId(1) val uin: Long = 0L,
-        @SerialId(2) val msgGroupInfo: List<Oidb0xc6c.GroupInfo>? = null
+        @SerialId(2) val msgGroupInfo: List<GroupInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1373,8 +1373,8 @@ class Oidb0xc05 : ProtoBuf {
     @Serializable
     class RspBody(
         @SerialId(1) val wording: String = "",
-        @SerialId(10) val getCreateAppListRsp: Oidb0xc05.GetCreateAppListRsp? = null,
-        @SerialId(11) val getAuthAppListRsp: Oidb0xc05.GetAuthAppListRsp? = null
+        @SerialId(10) val getCreateAppListRsp: GetCreateAppListRsp? = null,
+        @SerialId(11) val getAuthAppListRsp: GetAuthAppListRsp? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1392,8 +1392,8 @@ class Oidb0xc05 : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(10) val getCreateAppListReq: Oidb0xc05.GetCreateAppListReq? = null,
-        @SerialId(11) val getAuthAppListReq: Oidb0xc05.GetAuthAppListReq? = null
+        @SerialId(10) val getCreateAppListReq: GetCreateAppListReq? = null,
+        @SerialId(11) val getAuthAppListReq: GetAuthAppListReq? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1425,8 +1425,8 @@ class Cmd0x7da : ProtoBuf {
 class Qqconnect : ProtoBuf {
     @Serializable
     class MobileAppInfo(
-        @SerialId(11) val androidAppInfo: List<Qqconnect.AndroidAppInfo>? = null,
-        @SerialId(12) val iosAppInfo: List<Qqconnect.IOSAppInfo>? = null
+        @SerialId(11) val androidAppInfo: List<AndroidAppInfo>? = null,
+        @SerialId(12) val iosAppInfo: List<IOSAppInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1450,7 +1450,7 @@ class Qqconnect : ProtoBuf {
         @SerialId(10) val sourceUrl: String = "",
         @SerialId(11) val iconSmallUrl: String = "",
         @SerialId(12) val iconMiddleUrl: String = "",
-        @SerialId(13) val tencentDocsAppinfo: Qqconnect.TencentDocsAppinfo? = null,
+        @SerialId(13) val tencentDocsAppinfo: TencentDocsAppinfo? = null,
         @SerialId(21) val developerUin: Long = 0L,
         @SerialId(22) val appClass: Int = 0,
         @SerialId(23) val appSubclass: Int = 0,
@@ -1462,10 +1462,10 @@ class Qqconnect : ProtoBuf {
         @SerialId(29) val qqconnectFeature: Int = 0,
         @SerialId(30) val isHatchery: Int = 0,
         @SerialId(31) val testUinList: List<Long>? = null,
-        @SerialId(100) val templateMsgConfig: Qqconnect.TemplateMsgConfig? = null,
-        @SerialId(101) val miniAppInfo: Qqconnect.MiniAppInfo? = null,
-        @SerialId(102) val webAppInfo: Qqconnect.WebAppInfo? = null,
-        @SerialId(103) val mobileAppInfo: Qqconnect.MobileAppInfo? = null
+        @SerialId(100) val templateMsgConfig: TemplateMsgConfig? = null,
+        @SerialId(101) val miniAppInfo: MiniAppInfo? = null,
+        @SerialId(102) val webAppInfo: WebAppInfo? = null,
+        @SerialId(103) val mobileAppInfo: MobileAppInfo? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1561,8 +1561,8 @@ class Oidb0xc26 : ProtoBuf {
     class RgoupLabel(
         @SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(2) val enumType: Int /* enum */ = 1,
-        @SerialId(3) val textColor: Oidb0xc26.RgroupColor? = null,
-        @SerialId(4) val edgingColor: Oidb0xc26.RgroupColor? = null,
+        @SerialId(3) val textColor: RgroupColor? = null,
+        @SerialId(4) val edgingColor: RgroupColor? = null,
         @SerialId(5) val labelAttr: Int = 0,
         @SerialId(6) val labelType: Int = 0
     ) : ProtoBuf
@@ -1576,8 +1576,8 @@ class Oidb0xc26 : ProtoBuf {
     @Serializable
     class Label(
         @SerialId(1) val name: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val textColor: Oidb0xc26.Color? = null,
-        @SerialId(3) val edgingColor: Oidb0xc26.Color? = null,
+        @SerialId(2) val textColor: Color? = null,
+        @SerialId(3) val edgingColor: Color? = null,
         @SerialId(4) val labelType: Int = 0
     ) : ProtoBuf
 
@@ -1589,19 +1589,19 @@ class Oidb0xc26 : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val msgPersons: List<Oidb0xc26.MayKnowPerson>? = null,
+        @SerialId(1) val msgPersons: List<MayKnowPerson>? = null,
         @SerialId(2) val entryInuse: List<Int> = listOf(),
         @SerialId(3) val entryClose: List<Int> = listOf(),
         @SerialId(4) val nextGap: Int = 0,
         @SerialId(5) val timestamp: Int = 0,
         @SerialId(6) val msgUp: Int = 0,
-        @SerialId(7) val entryDelays: List<Oidb0xc26.EntryDelay>? = null,
+        @SerialId(7) val entryDelays: List<EntryDelay>? = null,
         @SerialId(8) val listSwitch: Int = 0,
         @SerialId(9) val addPageListSwitch: Int = 0,
         @SerialId(10) val emRspDataType: Int /* enum */ = 1,
-        @SerialId(11) val msgRgroupItems: List<Oidb0xc26.RecommendInfo>? = null,
+        @SerialId(11) val msgRgroupItems: List<RecommendInfo>? = null,
         @SerialId(12) val boolIsNewuser: Boolean = false,
-        @SerialId(13) val msgTables: List<Oidb0xc26.TabInfo>? = null,
+        @SerialId(13) val msgTables: List<TabInfo>? = null,
         @SerialId(14) val cookies: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
@@ -1612,15 +1612,15 @@ class Oidb0xc26 : ProtoBuf {
         @SerialId(3) val tableName: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(4) val iconUrlSelect: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(5) val iconUrlUnselect: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(6) val backgroundColorSelect: Oidb0xc26.Color? = null,
-        @SerialId(7) val backgroundColorUnselect: Oidb0xc26.Color? = null
+        @SerialId(6) val backgroundColorSelect: Color? = null,
+        @SerialId(7) val backgroundColorUnselect: Color? = null
     ) : ProtoBuf
 
     @Serializable
     class MayKnowPerson(
         @SerialId(1) val uin: Long = 0L,
-        @SerialId(2) val msgIosSource: Oidb0xc26.AddFriendSource? = null,
-        @SerialId(3) val msgAndroidSource: Oidb0xc26.AddFriendSource? = null,
+        @SerialId(2) val msgIosSource: AddFriendSource? = null,
+        @SerialId(3) val msgAndroidSource: AddFriendSource? = null,
         @SerialId(4) val reason: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(5) val additive: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(6) val nick: ByteArray = EMPTY_BYTE_ARRAY,
@@ -1637,14 +1637,14 @@ class Oidb0xc26 : ProtoBuf {
         @SerialId(17) val mobileName: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(18) val token: String = "",
         @SerialId(19) val onlineState: Int = 0,
-        @SerialId(20) val msgLabels: List<Oidb0xc26.Label>? = null,
+        @SerialId(20) val msgLabels: List<Label>? = null,
         @SerialId(21) val sourceid: Int = 0
     ) : ProtoBuf
 
     @Serializable
     class RecommendInfo(
         @SerialId(1) val woring: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(2) val msgGroups: List<Oidb0xc26.RgroupInfo>? = null
+        @SerialId(2) val msgGroups: List<RgroupInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1654,11 +1654,11 @@ class Oidb0xc26 : ProtoBuf {
         @SerialId(3) val groupName: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(4) val groupMemo: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(5) val memberNum: Int = 0,
-        @SerialId(6) val groupLabel: List<Oidb0xc26.RgoupLabel>? = null,
+        @SerialId(6) val groupLabel: List<RgoupLabel>? = null,
         @SerialId(7) val groupFlagExt: Int = 0,
         @SerialId(8) val groupFlag: Int = 0,
         @SerialId(9) val source: Int /* enum */ = 1,
-        @SerialId(10) val tagWording: Oidb0xc26.RgoupLabel? = null,
+        @SerialId(10) val tagWording: RgoupLabel? = null,
         @SerialId(11) val algorithm: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(12) val joinGroupAuth: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(13) val activity: Int = 0,
@@ -1697,7 +1697,7 @@ class Oidb0xc26 : ProtoBuf {
 class Cmd0xac6 : ProtoBuf {
     @Serializable
     class RspBody(
-        @SerialId(1) val results: List<Cmd0xac6.OperateResult>? = null,
+        @SerialId(1) val results: List<OperateResult>? = null,
         @SerialId(4) val metalCount: Int = 0,
         @SerialId(5) val metalTotal: Int = 0,
         @SerialId(9) val int32NewCount: Int = 0,
@@ -1707,7 +1707,7 @@ class Cmd0xac6 : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val medals: List<Cmd0xac6.MedalReport>? = null,
+        @SerialId(1) val medals: List<MedalReport>? = null,
         @SerialId(2) val clean: Int = 0
     ) : ProtoBuf
 
@@ -1788,12 +1788,12 @@ class Cmd0xac7 : ProtoBuf {
         @SerialId(1) val cmd: Int = 0,
         @SerialId(2) val din: Long = 0L,
         @SerialId(3) val extd: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(4) val msgBinderSig: Cmd0xac7.BinderSig? = null
+        @SerialId(4) val msgBinderSig: BinderSig? = null
     ) : ProtoBuf
 
     @Serializable
     class ReceiveMessageDevices(
-        @SerialId(1) val devices: List<Cmd0xac7.DeviceInfo>? = null
+        @SerialId(1) val devices: List<DeviceInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1836,22 +1836,22 @@ class FavoriteCKVData : ProtoBuf {
         @SerialId(7) val height: Int = 0,
         @SerialId(8) val size: Int = 0,
         @SerialId(9) val type: Int = 0,
-        @SerialId(10) val msgOwner: FavoriteCKVData.Author? = null,
+        @SerialId(10) val msgOwner: Author? = null,
         @SerialId(11) val picId: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
     @Serializable
     class KandianFavoriteItem(
-        @SerialId(1) val msgFavoriteExtInfo: FavoriteCKVData.KandianFavoriteBizData? = null,
+        @SerialId(1) val msgFavoriteExtInfo: KandianFavoriteBizData? = null,
         @SerialId(2) val bytesCid: List<ByteArray>? = null,
         @SerialId(3) val type: Int = 0,
         @SerialId(4) val status: Int = 0,
-        @SerialId(5) val msgAuthor: FavoriteCKVData.Author? = null,
+        @SerialId(5) val msgAuthor: Author? = null,
         @SerialId(6) val createTime: Long = 0L,
         @SerialId(7) val favoriteTime: Long = 0L,
         @SerialId(8) val modifyTime: Long = 0L,
         @SerialId(9) val dataSyncTime: Long = 0L,
-        @SerialId(10) val msgFavoriteSummary: FavoriteCKVData.FavoriteSummary? = null
+        @SerialId(10) val msgFavoriteSummary: FavoriteSummary? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1860,7 +1860,7 @@ class FavoriteCKVData : ProtoBuf {
         @SerialId(2) val title: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(3) val publisher: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(4) val brief: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(5) val msgPicInfo: List<FavoriteCKVData.PicInfo>? = null,
+        @SerialId(5) val msgPicInfo: List<PicInfo>? = null,
         @SerialId(6) val type: ByteArray = EMPTY_BYTE_ARRAY,
         @SerialId(7) val resourceUri: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
@@ -1869,18 +1869,18 @@ class FavoriteCKVData : ProtoBuf {
     class UserFavoriteList(
         @SerialId(1) val uin: Long = 0L,
         @SerialId(2) val modifyTs: Long = 0L,
-        @SerialId(100) val msgFavoriteItems: List<FavoriteCKVData.FavoriteItem>? = null
+        @SerialId(100) val msgFavoriteItems: List<FavoriteItem>? = null
     ) : ProtoBuf
 
     @Serializable
     class FavoriteSummary(
-        @SerialId(2) val msgLinkSummary: FavoriteCKVData.LinkSummary? = null
+        @SerialId(2) val msgLinkSummary: LinkSummary? = null
     ) : ProtoBuf
 
     @Serializable
     class FavoriteItem(
         @SerialId(1) val favoriteSource: Int = 0,
-        @SerialId(100) val msgKandianFavoriteItem: FavoriteCKVData.KandianFavoriteItem? = null
+        @SerialId(100) val msgKandianFavoriteItem: KandianFavoriteItem? = null
     ) : ProtoBuf
 
     @Serializable
@@ -1939,7 +1939,7 @@ class Oidb0xccd : ProtoBuf {
     @Serializable
     class RspBody(
         @SerialId(1) val errcode: Int = 0,
-        @SerialId(2) val results: List<Oidb0xccd.Result>? = null
+        @SerialId(2) val results: List<Result>? = null
     ) : ProtoBuf
 }
 
@@ -1979,7 +1979,7 @@ class Oidb0x87c : ProtoBuf {
 class Cmd0xbf2 : ProtoBuf {
     @Serializable
     class RspBody(
-        @SerialId(1) val phoneAddrBook: List<Cmd0xbf2.PhoneAddrBook>? = null,
+        @SerialId(1) val phoneAddrBook: List<PhoneAddrBook>? = null,
         @SerialId(2) val end: Int = 0,
         @SerialId(3) val nextIndex: Long = 0
     ) : ProtoBuf
@@ -2014,10 +2014,10 @@ class Cmd0x6cd : ProtoBuf {
         @SerialId(8) val lastRecvTime: Int = 0,
         @SerialId(9) val fromId: Long = 0L,
         @SerialId(10) val enumRedpointType: Int /* enum */ = 1,
-        @SerialId(11) val msgRedpointExtraInfo: Cmd0x6cd.RepointExtraInfo? = null,
+        @SerialId(11) val msgRedpointExtraInfo: RepointExtraInfo? = null,
         @SerialId(12) val configVersion: String = "",
         @SerialId(13) val doActivity: Int = 0,
-        @SerialId(14) val msgUnreadMsg: List<Cmd0x6cd.MessageRec>? = null
+        @SerialId(14) val msgUnreadMsg: List<MessageRec>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2028,15 +2028,15 @@ class Cmd0x6cd : ProtoBuf {
 
     @Serializable
     class RspBody(
-        @SerialId(1) val msgRedpoint: List<Cmd0x6cd.RedpointInfo>? = null,
-        @SerialId(2) val unfinishedRedpoint: List<Cmd0x6cd.PullRedpointReq>? = null
+        @SerialId(1) val msgRedpoint: List<RedpointInfo>? = null,
+        @SerialId(2) val unfinishedRedpoint: List<PullRedpointReq>? = null
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val lastPullRedpoint: List<Cmd0x6cd.PullRedpointReq>? = null,
-        @SerialId(2) val unfinishedRedpoint: List<Cmd0x6cd.PullRedpointReq>? = null,
-        @SerialId(3) val msgPullSingleTask: Cmd0x6cd.PullRedpointReq? = null,
+        @SerialId(1) val lastPullRedpoint: List<PullRedpointReq>? = null,
+        @SerialId(2) val unfinishedRedpoint: List<PullRedpointReq>? = null,
+        @SerialId(3) val msgPullSingleTask: PullRedpointReq? = null,
         @SerialId(4) val retMsgRec: Int = 0
     ) : ProtoBuf
 
@@ -2072,9 +2072,9 @@ class Oidb0xd55 : ProtoBuf {
         @SerialId(2) val appType: Int = 0,
         @SerialId(3) val srcId: Int = 0,
         @SerialId(4) val rawUrl: String = "",
-        @SerialId(11) val checkAppSignReq: Oidb0xd55.CheckAppSignReq? = null,
-        @SerialId(12) val checkUserReq: Oidb0xd55.CheckUserReq? = null,
-        @SerialId(13) val checkMiniAppReq: Oidb0xd55.CheckMiniAppReq? = null
+        @SerialId(11) val checkAppSignReq: CheckAppSignReq? = null,
+        @SerialId(12) val checkUserReq: CheckUserReq? = null,
+        @SerialId(13) val checkMiniAppReq: CheckMiniAppReq? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2085,9 +2085,9 @@ class Oidb0xd55 : ProtoBuf {
     @Serializable
     class RspBody(
         @SerialId(1) val wording: String = "",
-        @SerialId(11) val checkAppSignRsp: Oidb0xd55.CheckAppSignRsp? = null,
-        @SerialId(12) val checkUserRsp: Oidb0xd55.CheckUserRsp? = null,
-        @SerialId(13) val checkMiniAppRsp: Oidb0xd55.CheckMiniAppRsp? = null
+        @SerialId(11) val checkAppSignRsp: CheckAppSignRsp? = null,
+        @SerialId(12) val checkUserRsp: CheckUserRsp? = null,
+        @SerialId(13) val checkMiniAppRsp: CheckMiniAppRsp? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2148,7 +2148,7 @@ class Cmd0x8b4 : ProtoBuf {
         @SerialId(1) val result: Int = 0,
         @SerialId(2) val flag: Int = 0,
         @SerialId(21) val tag: ByteArray = EMPTY_BYTE_ARRAY,
-        @SerialId(22) val groupInfo: List<Cmd0x8b4.GroupInfo>? = null,
+        @SerialId(22) val groupInfo: List<GroupInfo>? = null,
         @SerialId(23) val textLabel: List<ByteArray>? = null
     ) : ProtoBuf
 }
@@ -2157,7 +2157,7 @@ class Cmd0x8b4 : ProtoBuf {
 class Cmd0x682 : ProtoBuf {
     @Serializable
     class RspBody(
-        @SerialId(1) val msgChatinfo: List<Cmd0x682.ChatInfo>? = null
+        @SerialId(1) val msgChatinfo: List<ChatInfo>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2200,7 +2200,7 @@ class Cmd0x6f5 : ProtoBuf {
     @Serializable
     class RspBody(
         @SerialId(1) val configVersion: String = "",
-        @SerialId(2) val taskInfo: List<Cmd0x6f5.TaskInfo>? = null
+        @SerialId(2) val taskInfo: List<TaskInfo>? = null
     ) : ProtoBuf
 }
 
@@ -2208,7 +2208,7 @@ class Cmd0x6f5 : ProtoBuf {
 class Oidb0xb7e : ProtoBuf {
     @Serializable
     class RspBody(
-        @SerialId(1) val topItem: List<Oidb0xb7e.DiandianTopConfig>? = null
+        @SerialId(1) val topItem: List<DiandianTopConfig>? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2230,7 +2230,7 @@ class Oidb0xb7e : ProtoBuf {
 class Oidb0xc2f : ProtoBuf {
     @Serializable
     class RspBody(
-        @SerialId(1) val msgGetFollowUserRecommendListRsp: Oidb0xc2f.GetFollowUserRecommendListRsp? = null
+        @SerialId(1) val msgGetFollowUserRecommendListRsp: GetFollowUserRecommendListRsp? = null
     ) : ProtoBuf
 
     @Serializable
@@ -2251,13 +2251,13 @@ class Oidb0xc2f : ProtoBuf {
 
     @Serializable
     class GetFollowUserRecommendListRsp(
-        @SerialId(1) val msgRecommendList: List<Oidb0xc2f.RecommendAccountInfo>? = null,
+        @SerialId(1) val msgRecommendList: List<RecommendAccountInfo>? = null,
         @SerialId(2) val jumpUrl: ByteArray = EMPTY_BYTE_ARRAY
     ) : ProtoBuf
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val msgGetFollowUserRecommendListReq: Oidb0xc2f.GetFollowUserRecommendListReq? = null
+        @SerialId(1) val msgGetFollowUserRecommendListReq: GetFollowUserRecommendListReq? = null
     ) : ProtoBuf
 }
 
@@ -2288,7 +2288,7 @@ class Cmd0xd40 : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val dev: Cmd0xd40.DeviceInfo? = null,
+        @SerialId(1) val dev: DeviceInfo? = null,
         @SerialId(2) val src: Int = 0,
         @SerialId(3) val event: Int = 0,
         @SerialId(4) val redtype: Int = 0
@@ -2312,7 +2312,7 @@ class Cmd0x6ce : ProtoBuf {
 
     @Serializable
     class ReqBody(
-        @SerialId(1) val msgReadReq: List<Cmd0x6ce.ReadRedpointReq>? = null
+        @SerialId(1) val msgReadReq: List<ReadRedpointReq>? = null
     ) : ProtoBuf
 }
 
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
similarity index 100%
rename from mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.PbPushGroupMsg.kt
rename to mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/OnlinePush.kt
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
index 13025fed3..6db064f39 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt
@@ -19,24 +19,40 @@ import kotlin.contracts.contract
  */
 interface Contact : CoroutineScope {
     /**
-     * 这个联系人所属 [Bot]
+     * 这个联系人所属 [Bot].
      */
     @WeakRefProperty
     val bot: Bot // weak ref
 
     /**
      * 可以是 QQ 号码或者群号码.
+     *
+     * 对于 QQ, `uin` 与 `id` 是相同的意思.
+     * 对于 Group, `groupCode` 与 `id` 是相同的意思.
      */
     val id: Long
 
     /**
      * 向这个对象发送消息.
-     *
-     * 速度太快会被服务器屏蔽(无响应). 在测试中不延迟地发送 6 条消息就会被屏蔽之后的数据包 1 秒左右.
      */
     suspend fun sendMessage(message: MessageChain)
 
+    /**
+     * 上传一个图片以备发送.
+     * TODO: 群图片与好友图片之间是否通用还不确定.
+     * TODO: 好友之间图片是否通用还不确定.
+     */
     suspend fun uploadImage(image: ExternalImage): Image
+
+    /**
+     * 判断 `this` 和 [other] 是否是相同的类型, 并且 [id] 相同.
+     *
+     * 注:
+     * [id] 相同的 [Member] 和 [QQ], 他们并不 [equals].
+     * 因为, [Member] 含义为群员, 必属于一个群.
+     * 而 [QQ] 含义为一个独立的人, 可以是好友, 也可以是陌生人.
+     */
+    override fun equals(other: Any?): Boolean
 }
 
 suspend inline fun Contact.sendMessage(message: Message) = sendMessage(message.toChain())
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
index 3f13b89af..46174e03f 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
@@ -9,38 +9,38 @@ import kotlinx.coroutines.CoroutineScope
  * 群. 在 QQ Android 中叫做 "Troop"
  */
 interface Group : Contact, CoroutineScope {
-
     /**
-     * ====以下字段在更新值的时候会自动异步上报服务器更改群信息====
-     */
-    /**
-     * 群名称
-     * [可查可改已完成]
+     * 群名称.
+     *
+     * 在修改时将会异步上传至服务器.
+     *
+     * 注: 频繁修改可能会被服务器拒绝
      */
     var name: String
     /**
-     * 入群公告, 没有时为空字符串
-     * [可查可改已完成]
+     * 入群公告, 没有时为空字符串.
+     *
+     * 在修改时将会异步上传至服务器.
      */
     var announcement: String
     /**
-     * 全体禁言状态
-     * [可改已完成]
-     */
+     * 全体禁言状态. `true` 为开启.
+     *
+     * 当前仅能修改状态.
+     */// TODO: 2020/2/5 实现 muteAll 的查询
     var muteAll: Boolean
     /**
-     * 坦白说状态
-     * [可查可改已完成]
+     * 坦白说状态. `true` 为允许.
+     *
+     * 在修改时将会异步上传至服务器.
      */
     var confessTalk: Boolean
     /**
-     * 允许群员拉人状态
-     * [可查可改已完成]
+     * 允许群员邀请好友入群的状态. `true` 为允许
      */
     var allowMemberInvite: Boolean
     /**
-     * QQ中的自动加群审批
-     * [可查已完成]
+     * 自动加群审批
      */
     val autoApprove: Boolean
     /**
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
index b522a910e..b8a7e3f86 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Member.kt
@@ -11,18 +11,18 @@ import kotlin.time.ExperimentalTime
  */
 interface Member : QQ, Contact {
     /**
-     * 所在的群
+     * 所在的群.
      */
     @WeakRefProperty
     val group: Group
 
     /**
-     * 权限
+     * 成员的权限, 动态更新.
      */
     val permission: MemberPermission
 
     /**
-     *
+     * 群名片 (如果有) 或个人昵称. 动态更新.
      */
     var groupCard: String
 
@@ -30,7 +30,7 @@ interface Member : QQ, Contact {
      * 禁言
      *
      * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
-     * @return 若机器人无权限禁言这个群成员, 返回 `false`
+     * @return 仅当机器人无权限禁言这个群成员时返回 `false`
      *
      * @see Int.minutesToSeconds
      * @see Int.hoursToSeconds
@@ -39,10 +39,14 @@ interface Member : QQ, Contact {
     suspend fun mute(durationSeconds: Int): Boolean
 
     /**
-     * 解除禁言
+     * 解除禁言. 在没有权限时会返回 `false`. 否则均返回 `true`.
      */
     suspend fun unmute(): Boolean
 
+    /**
+     * 当且仅当 `[other] is [Member] && [other].id == this.id && [other].group == this.group` 时为 true
+     */
+    override fun equals(other: Any?): Boolean
 }
 
 @ExperimentalTime
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
index 10450a1ee..b8fc20f03 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessagePacket.kt
@@ -3,6 +3,7 @@
 package net.mamoe.mirai.message
 
 import kotlinx.io.core.ByteReadPacket
+import kotlinx.io.core.IoBuffer
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.contact.Group
@@ -15,35 +16,47 @@ import net.mamoe.mirai.utils.*
 import kotlin.jvm.JvmName
 
 /**
- * 平台相关扩展
+ * 一条从服务器接收到的消息事件.
+ * 请查看各平台的 `actual` 实现的说明.
  */
 @UseExperimental(MiraiInternalAPI::class)
 expect abstract class MessagePacket<TSender : QQ, TSubject : Contact>(bot: Bot) : MessagePacketBase<TSender, TSubject>
 
+/**
+ * 仅内部使用, 请使用 [MessagePacket]
+ */ // Tips: 在 IntelliJ 中 (左侧边栏) 打开 `Structure`, 可查看类结构
 @Suppress("NOTHING_TO_INLINE")
 @MiraiInternalAPI
 abstract class MessagePacketBase<TSender : QQ, TSubject : Contact>(_bot: Bot) : EventPacket, BotEvent() {
+    /**
+     * 接受到这条消息的
+     */
     override val bot: Bot by _bot.unsafeWeakRef()
 
     /**
      * 消息事件主体.
      *
-     * 对于好友消息, 这个属性为 [QQ] 的实例;
-     * 对于群消息, 这个属性为 [Group] 的实例
+     * 对于好友消息, 这个属性为 [QQ] 的实例, 与 [sender] 引用相同;
+     * 对于群消息, 这个属性为 [Group] 的实例, 与 [GroupMessage.group] 引用相同
      *
      * 在回复消息时, 可通过 [subject] 作为回复对象
      */
     abstract val subject: TSubject
 
     /**
-     * 发送人
+     * 发送人.
+     *
+     * 在好友消息时为 [QQ] 的实例, 在群消息时为 [Member] 的实例
      */
     abstract val sender: TSender
 
+    /**
+     * 消息内容
+     */
     abstract val message: MessageChain
 
 
-    // region Send to subject
+    // region 发送 Message
 
     /**
      * 给这个消息事件的主体发送消息
@@ -64,20 +77,41 @@ abstract class MessagePacketBase<TSender : QQ, TSubject : Contact>(_bot: Bot) :
     @JvmName("reply1")
     suspend inline fun MessageChain.reply() = reply(this)
 
+    // endregion
+
+    // region 上传图片
+    suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
+    // endregion
+
+    // region 发送图片
     suspend inline fun ExternalImage.send() = this.sendTo(subject)
 
-    suspend inline fun ExternalImage.upload(): Image = this.upload(subject)
     suspend inline fun Image.send() = this.sendTo(subject)
     suspend inline fun Message.send() = this.sendTo(subject)
     suspend inline fun String.send() = this.toMessage().sendTo(subject)
+    // endregion
 
-    inline fun QQ.at(): At = At(this as Member)
+    /**
+     * 创建 @ 这个账号的消息. 当且仅当消息为群消息时可用. 否则将会抛出 [IllegalArgumentException]
+     */
+    inline fun QQ.at(): At = At(this as? Member ?: error("`QQ.at` can only be used in GroupMessage"))
 
     // endregion
 
     // region Image download
+    /**
+     * 将图片下载到内存.
+     *
+     * 非常不推荐这样做.
+     */
+    @Deprecated("内存使用效率十分低下", ReplaceWith("this.download()"), DeprecationLevel.WARNING)
     suspend inline fun Image.downloadAsByteArray(): ByteArray = bot.run { downloadAsByteArray() }
 
+    // TODO: 2020/2/5 为下载图片添加文件系统的存储方式
+
+    /**
+     * 将图片下载到内存缓存中 (使用 [IoBuffer.Pool])
+     */
     suspend inline fun Image.download(): ByteReadPacket = bot.run { download() }
     // endregion
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
index 4576871e1..db5e54398 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt
@@ -17,6 +17,8 @@ import kotlin.reflect.KProperty
  * - 若两个 [MessageChain] 连接, 后一个将会被合并到第一个内.
  * - 若一个 [MessageChain] 与一个其他 [Message] 连接, [Message] 将会被添加入 [MessageChain].
  * - 若一个 [Message] 与一个 [MessageChain] 连接, [Message] 将会被添加入 [MessageChain].
+ *
+ * 要获取更多信息, 请查看 [Message]
  */
 interface MessageChain : Message, MutableList<Message> {
     // region Message override
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
index 7cd1dd3bb..36a9b56cf 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/utils/io/DebugUtil.kt
@@ -70,6 +70,7 @@ fun ByteReadPacket.debugPrintThis(name: String = ""): ByteReadPacket {
 @MiraiDebugAPI("Low efficiency")
 @UseExperimental(ExperimentalContracts::class)
 inline fun <R> Input.debugIfFail(name: String = "", onFail: (ByteArray) -> ByteReadPacket = { it.toReadPacket() }, block: ByteReadPacket.() -> R): R {
+
     contract {
         callsInPlace(block, InvocationKind.EXACTLY_ONCE)
         callsInPlace(onFail, InvocationKind.UNKNOWN)
diff --git a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
index 26bc03188..b0d33fb02 100644
--- a/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
+++ b/mirai-core/src/jvmMain/kotlin/net/mamoe/mirai/message/MessagePacket.kt
@@ -22,37 +22,52 @@ import java.net.URL
 import javax.imageio.ImageIO
 
 /**
+ * 一条从服务器接收到的消息事件.
  * JVM 平台相关扩展
  */
 @UseExperimental(MiraiInternalAPI::class)
 actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual constructor(bot: Bot) : MessagePacketBase<TSender, TSubject>(bot) {
+    // region 上传图片
     suspend inline fun uploadImage(image: BufferedImage): Image = subject.uploadImage(image)
+
     suspend inline fun uploadImage(image: URL): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: Input): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: InputStream): Image = subject.uploadImage(image)
     suspend inline fun uploadImage(image: File): Image = subject.uploadImage(image)
+    // endregion
 
+    // region 发送图片
     suspend inline fun sendImage(image: BufferedImage) = subject.sendImage(image)
+
     suspend inline fun sendImage(image: URL) = subject.sendImage(image)
     suspend inline fun sendImage(image: Input) = subject.sendImage(image)
     suspend inline fun sendImage(image: InputStream) = subject.sendImage(image)
     suspend inline fun sendImage(image: File) = subject.sendImage(image)
+    // endregion
 
+    // region 上传图片 (扩展)
     suspend inline fun BufferedImage.upload(): Image = upload(subject)
+
     suspend inline fun URL.uploadAsImage(): Image = uploadAsImage(subject)
     suspend inline fun Input.uploadAsImage(): Image = uploadAsImage(subject)
     suspend inline fun InputStream.uploadAsImage(): Image = uploadAsImage(subject)
     suspend inline fun File.uploadAsImage(): Image = uploadAsImage(subject)
+    // endregion 上传图片 (扩展)
 
+    // region 发送图片 (扩展)
     suspend inline fun BufferedImage.send() = sendTo(subject)
+
     suspend inline fun URL.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun Input.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun InputStream.sendAsImage() = sendAsImageTo(subject)
     suspend inline fun File.sendAsImage() = sendAsImageTo(subject)
+    // endregion 发送图片 (扩展)
 
+    // region 下载图片 (扩展)
     suspend inline fun Image.downloadTo(file: File): Long = file.outputStream().use { downloadTo(it) }
+
     /**
-     * 这个函数结束后不会关闭 [output]
+     * 这个函数结束后不会关闭 [output]. 请务必解决好 [OutputStream.close]
      */
     suspend inline fun Image.downloadTo(output: OutputStream): Long =
         download().inputStream().use { input -> withContext(Dispatchers.IO) { input.copyTo(output) } }
@@ -60,4 +75,5 @@ actual abstract class MessagePacket<TSender : QQ, TSubject : Contact> actual con
     suspend inline fun Image.downloadAsStream(): InputStream = download().inputStream()
     suspend inline fun Image.downloadAsExternalImage(): ExternalImage = withContext(Dispatchers.IO) { download().toExternalImage() }
     suspend inline fun Image.downloadAsBufferedImage(): BufferedImage = withContext(Dispatchers.IO) { ImageIO.read(downloadAsStream()) }
+    // endregion
 }
\ No newline at end of file
diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
index a661934cf..e14698248 100644
--- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
+++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/GentleImage.kt
@@ -1,16 +1,15 @@
 package demo.gentleman
 
 import com.alibaba.fastjson.JSON
+import com.alibaba.fastjson.JSONObject
 import kotlinx.coroutines.*
 import net.mamoe.mirai.contact.Contact
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.uploadAsImage
 import org.jsoup.Jsoup
+import kotlin.random.Random
 
-class GentleImage {
-    lateinit var contact: Contact
-
-    // `Deferred<Image?>`  causes a runtime ClassCastException
+class GentleImage(val contact: Contact, val keyword: String) {
 
     val image: Deferred<Image> by lazy { getImage(0) }
 
@@ -18,18 +17,21 @@ class GentleImage {
 
     fun getImage(r18: Int): Deferred<Image> {
         return GlobalScope.async {
-            withTimeoutOrNull(5 * 1000) {
+            withTimeoutOrNull(10 * 1000) {
                 withContext(Dispatchers.IO) {
                     val result =
                         JSON.parseObject(
-                            Jsoup.connect("https://api.lolicon.app/setu/?r18=$r18").ignoreContentType(true).timeout(
+                            Jsoup.connect("https://api.lolicon.app/setu/?r18=$r18" + if (keyword.isNotBlank()) "&keyword=$keyword&num=100" else "").ignoreContentType(
+                                true
+                            ).timeout(
                                 10_0000
                             ).get().body().text()
                         )
 
                     val url: String
                     val pid: String
-                    with(result.getJSONArray("data").getJSONObject(0)) {
+                    val data = result.getJSONArray("data")
+                    with(JSONObject(data.getJSONObject(Random.nextInt(0, data.size)))) {
                         url = this.getString("url")
                         pid = this.getString("pid")
                     }
diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Gentleman.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Gentleman.kt
index b1fcc6056..f54f9cd39 100644
--- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Gentleman.kt
+++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Gentleman.kt
@@ -18,22 +18,20 @@ private const val IMAGE_BUFFER_CAPACITY: Int = 5
 @ExperimentalUnsignedTypes
 @ExperimentalCoroutinesApi
 object Gentlemen : MutableMap<Long, Gentleman> by mutableMapOf() {
-    fun provide(key: Contact): Gentleman = this.getOrPut(key.id) { Gentleman(key) }
+    fun provide(key: Contact, keyword: String = ""): Gentleman = this.getOrPut(key.id) { Gentleman(key, keyword) }
 }
 
 /**
  * 工作是缓存图片
  */
 @ExperimentalCoroutinesApi
-class Gentleman(private val contact: Contact) : Channel<GentleImage> by Channel(IMAGE_BUFFER_CAPACITY) {
+class Gentleman(private val contact: Contact, private val keyword: String) : Channel<GentleImage> by Channel(IMAGE_BUFFER_CAPACITY) {
     init {
 
         GlobalScope.launch {
             while (!isClosedForSend) {
-                send(GentleImage().apply {
-                    contact = this@Gentleman.contact
-
-                    image// start downloading
+                send(GentleImage(contact, keyword).apply {
+                    seImage// start downloading
                 })
             }
         }