diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/GroupManageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/GroupManageRouteModule.kt
index fcf7c8e03..645cb741e 100644
--- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/GroupManageRouteModule.kt
+++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/GroupManageRouteModule.kt
@@ -64,7 +64,7 @@ fun Application.groupManageModule() {
             val group = dto.session.bot.getGroup(dto.target)
             with(dto.config) {
                 name?.let { group.name = it }
-                announcement?.let { group.announcement = it }
+                announcement?.let { group.entranceAnnouncement = it }
                 confessTalk?.let { group.confessTalk = it }
                 allowMemberInvite?.let { group.allowMemberInvite = it }
                 // TODO: 待core接口实现设置可改
@@ -128,7 +128,7 @@ private data class GroupDetailDTO(
     val anonymousChat: Boolean? = null
 ) : DTO {
     constructor(group: Group) : this(
-        group.name, group.announcement, group.confessTalk, group.allowMemberInvite,
+        group.name, group.entranceAnnouncement, group.confessTalk, group.allowMemberInvite,
         group.autoApprove, group.anonymousChat
     )
 }
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 e8a628fb9..1a5ffbf37 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
@@ -15,7 +15,9 @@ import net.mamoe.mirai.data.FriendNameRemark
 import net.mamoe.mirai.data.PreviousNameList
 import net.mamoe.mirai.data.Profile
 import net.mamoe.mirai.event.broadcast
-import net.mamoe.mirai.event.events.MemberCardChangeEvent
+import net.mamoe.mirai.event.events.*
+import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
+import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
 import net.mamoe.mirai.message.data.CustomFaceFromFile
 import net.mamoe.mirai.message.data.Image
 import net.mamoe.mirai.message.data.MessageChain
@@ -53,18 +55,25 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
     override lateinit var nick: String
 
     override suspend fun sendMessage(message: MessageChain) {
+        val event = FriendMessageSendEvent(this, message).broadcast()
+        if (event.isCancelled) {
+            throw EventCancelledException("cancelled by FriendMessageSendEvent")
+        }
         bot.network.run {
             check(
                 MessageSvc.PbSendMsg.ToFriend(
                     bot.client,
                     id,
-                    message
+                    event.message
                 ).sendAndExpect<MessageSvc.PbSendMsg.Response>() is MessageSvc.PbSendMsg.Response.SUCCESS
             ) { "send message failed" }
         }
     }
 
     override suspend fun uploadImage(image: ExternalImage): Image = try {
+        if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
+            throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
+        }
         bot.network.run {
             val response = LongConn.OffPicUp(
                 bot.client, Cmd0x352.TryUpImgReq(
@@ -89,7 +98,9 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
                     height = response.imageInfo.fileHeight,
                     width = response.imageInfo.fileWidth,
                     resourceId = response.resourceId
-                )
+                ).also {
+                    ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
+                }
                 is LongConn.OffPicUp.Response.RequireUpload -> {
                     Http.postImage("0x6ff0070", bot.uin, null, imageInput = image.input, inputSize = image.inputSize, uKeyHex = response.uKey.toUHexString(""))
 //                    HighwayHelper.uploadImage(
@@ -110,9 +121,14 @@ internal class QQImpl(bot: QQAndroidBot, override val coroutineContext: Coroutin
                         height = image.height,
                         width = image.width,
                         resourceId = response.resourceId
-                    )
+                    ).also {
+                        ImageUploadEvent.Succeed(this@QQImpl, image, it).broadcast()
+                    }
+                }
+                is LongConn.OffPicUp.Response.Failed -> {
+                    ImageUploadEvent.Failed(this@QQImpl, image, -1, response.message).broadcast()
+                    error(response.message)
                 }
-                is LongConn.OffPicUp.Response.Failed -> error(response.message)
             }
         }
     } finally {
@@ -159,7 +175,6 @@ internal class MemberImpl(
                 val oldValue = _groupCard
                 _groupCard = newValue
                 launch {
-                    MemberCardChangeEvent.ByBot(oldValue, newValue, this@MemberImpl).broadcast()
                     bot.network.run {
                         TroopManagement.EditGroupNametag(
                             bot.client,
@@ -167,6 +182,7 @@ internal class MemberImpl(
                             newValue
                         ).sendWithoutExpect()
                     }
+                    MemberCardChangeEvent.ByBot(oldValue, newValue, this@MemberImpl).broadcast()
                 }
             }
         }
@@ -176,6 +192,7 @@ internal class MemberImpl(
         set(newValue) {
             group.checkBotPermissionOperator()
             if (_specialTitle != newValue) {
+                val oldValue = _specialTitle
                 _specialTitle = newValue
                 launch {
                     bot.network.run {
@@ -185,6 +202,7 @@ internal class MemberImpl(
                             newValue
                         ).sendWithoutExpect()
                     }
+                    MemberSpecialTitleChangeEvent(oldValue, newValue, this@MemberImpl).broadcast()
                 }
             }
         }
@@ -204,6 +222,8 @@ internal class MemberImpl(
                 timeInSecond = durationSeconds
             ).sendAndExpect<TroopManagement.Mute.Response>()
         }
+
+        MemberMuteEvent.ByBot(this@MemberImpl, durationSeconds).broadcast()
         return true
     }
 
@@ -220,6 +240,8 @@ internal class MemberImpl(
                 timeInSecond = 0
             ).sendAndExpect<TroopManagement.Mute.Response>()
         }
+
+        MemberUnmuteEvent.ByBot(this@MemberImpl).broadcast()
         return true
     }
 
@@ -233,7 +255,9 @@ internal class MemberImpl(
                 client = bot.client,
                 member = this@MemberImpl,
                 message = message
-            ).sendAndExpect<TroopManagement.Kick.Response>().success
+            ).sendAndExpect<TroopManagement.Kick.Response>().success.also {
+                MemberLeaveEvent.Kick.ByBot(this@MemberImpl).broadcast()
+            }
         }
     }
 
@@ -270,6 +294,7 @@ internal class GroupImpl(
         set(newValue) {
             this.checkBotPermissionOperator()
             if (_name != newValue) {
+                val oldValue = _name
                 _name = newValue
                 launch {
                     bot.network.run {
@@ -279,15 +304,17 @@ internal class GroupImpl(
                             newName = newValue
                         ).sendWithoutExpect()
                     }
+                    GroupNameChangeEvent.ByBot(oldValue, newValue, this@GroupImpl).broadcast()
                 }
             }
         }
 
-    override var announcement: String
+    override var entranceAnnouncement: String
         get() = _announcement
         set(newValue) {
             this.checkBotPermissionOperator()
             if (_announcement != newValue) {
+                val oldValue = _announcement
                 _announcement = newValue
                 launch {
                     bot.network.run {
@@ -297,6 +324,7 @@ internal class GroupImpl(
                             newMemo = newValue
                         ).sendWithoutExpect()
                     }
+                    GroupEntranceAnnouncementChangeEvent.ByBot(oldValue, newValue, this@GroupImpl).broadcast()
                 }
             }
         }
@@ -307,6 +335,7 @@ internal class GroupImpl(
         set(newValue) {
             this.checkBotPermissionOperator()
             if (_allowMemberInvite != newValue) {
+                val oldValue = _allowMemberInvite
                 _allowMemberInvite = newValue
                 launch {
                     bot.network.run {
@@ -316,6 +345,7 @@ internal class GroupImpl(
                             switch = newValue
                         ).sendWithoutExpect()
                     }
+                    GroupAllowMemberInviteEvent.ByBot(oldValue, newValue, this@GroupImpl).broadcast()
                 }
             }
         }
@@ -337,6 +367,7 @@ internal class GroupImpl(
         set(newValue) {
             this.checkBotPermissionOperator()
             if (_confessTalk != newValue) {
+                val oldValue = _confessTalk
                 _confessTalk = newValue
                 launch {
                     bot.network.run {
@@ -346,6 +377,7 @@ internal class GroupImpl(
                             switch = newValue
                         ).sendWithoutExpect()
                     }
+                    GroupAllowConfessTalkEvent.ByBot(oldValue, newValue, this@GroupImpl).broadcast()
                 }
             }
         }
@@ -356,6 +388,7 @@ internal class GroupImpl(
         set(newValue) {
             this.checkBotPermissionOperator()
             if (_muteAll != newValue) {
+                val oldValue = _muteAll
                 _muteAll = newValue
                 launch {
                     bot.network.run {
@@ -365,6 +398,7 @@ internal class GroupImpl(
                             switch = newValue
                         ).sendWithoutExpect()
                     }
+                    GroupMuteAllEvent.ByBot(oldValue, newValue, this@GroupImpl).broadcast()
                 }
             }
         }
@@ -376,7 +410,7 @@ internal class GroupImpl(
 
     override suspend fun quit(): Boolean {
         check(botPermission != MemberPermission.OWNER) { "An owner cannot quit from a owning group" }
-        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
+        TODO("not implemented")
     }
 
 
@@ -395,11 +429,15 @@ internal class GroupImpl(
     override val bot: QQAndroidBot by bot.unsafeWeakRef()
 
     override suspend fun sendMessage(message: MessageChain) {
+        val event = GroupMessageSendEvent(this, message).broadcast()
+        if (event.isCancelled) {
+            throw EventCancelledException("cancelled by FriendMessageSendEvent")
+        }
         bot.network.run {
             val response = MessageSvc.PbSendMsg.ToGroup(
                 bot.client,
                 id,
-                message
+                event.message
             ).sendAndExpect<MessageSvc.PbSendMsg.Response>()
             check(
                 response is MessageSvc.PbSendMsg.Response.SUCCESS
@@ -408,6 +446,9 @@ internal class GroupImpl(
     }
 
     override suspend fun uploadImage(image: ExternalImage): Image = try {
+        if (BeforeImageUploadEvent(this, image).broadcast().isCancelled) {
+            throw EventCancelledException("cancelled by BeforeImageUploadEvent.ToGroup")
+        }
         bot.network.run {
             val response: ImgStore.GroupPicUp.Response = ImgStore.GroupPicUp(
                 bot.client,
@@ -422,7 +463,10 @@ internal class GroupImpl(
             ).sendAndExpect()
 
             when (response) {
-                is ImgStore.GroupPicUp.Response.Failed -> error("upload group image failed with reason ${response.message}")
+                is ImgStore.GroupPicUp.Response.Failed -> {
+                    ImageUploadEvent.Failed(this@GroupImpl, image, response.resultCode, response.message).broadcast()
+                    error("upload group image failed with reason ${response.message}")
+                }
                 is ImgStore.GroupPicUp.Response.FileExists -> {
                     val resourceId = image.calculateImageResourceId()
 //                    return NotOnlineImageFromFile(
@@ -439,10 +483,9 @@ internal class GroupImpl(
                     return CustomFaceFromFile(
                         md5 = image.md5,
                         filepath = resourceId
-                    )
+                    ).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
                 }
                 is ImgStore.GroupPicUp.Response.RequireUpload -> {
-
                     HighwayHelper.uploadImage(
                         client = bot.client,
                         serverIp = response.uploadIpList.first().toIpV4AddressString(),
@@ -467,7 +510,7 @@ internal class GroupImpl(
                     return CustomFaceFromFile(
                         md5 = image.md5,
                         filepath = resourceId
-                    )
+                    ).also { ImageUploadEvent.Succeed(this@GroupImpl, image, it).broadcast() }
                     /*
                         fileId = response.fileId.toInt(),
                         fileType = 0, // ?
diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
index 6074712cb..bedf67f9a 100644
--- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
+++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/TroopManagement.kt
@@ -27,7 +27,6 @@ import net.mamoe.mirai.qqandroid.network.protocol.packet.EMPTY_BYTE_ARRAY
 import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacket
 import net.mamoe.mirai.qqandroid.network.protocol.packet.OutgoingPacketFactory
 import net.mamoe.mirai.qqandroid.network.protocol.packet.buildOutgoingUniPacket
-import net.mamoe.mirai.qqandroid.network.protocol.packet.chat.TroopManagement.GetGroupOperationInfo.decode
 import net.mamoe.mirai.utils.daysToSeconds
 import net.mamoe.mirai.utils.io.encodeToString
 
@@ -66,9 +65,7 @@ internal class TroopManagement {
         }
 
         object Response : Packet {
-            override fun toString(): String {
-                return "Response(Mute)"
-            }
+            override fun toString(): String = "Response(Mute)"
         }
     }
 
@@ -80,9 +77,7 @@ internal class TroopManagement {
             val autoApprove: Boolean,
             val confessTalk: Boolean
         ) : Packet {
-            override fun toString(): String {
-                return "Response(GroupInfo)"
-            }
+            override fun toString(): String = "Response(GroupInfo)"
         }
 
         operator fun invoke(
@@ -146,9 +141,7 @@ internal class TroopManagement {
         class Response(
             val success: Boolean
         ) : Packet {
-            override fun toString(): String {
-                return "Response(Kick Member)"
-            }
+            override fun toString(): String = "Response(Kick Member)"
         }
 
         operator fun invoke(
@@ -424,13 +417,4 @@ internal class TroopManagement {
         }
 
     }
-
-    /*
-    internal object Recall: OutgoingPacketFactory<WtLogin.Login.LoginPacketResponse>("wtlogin.login"){
-        override suspend fun ByteReadPacket.decode(bot: QQAndroidBot): WtLogin.Login.LoginPacketResponse {
-            TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
-        }
-    }
-     */
-
 }
\ No newline at end of file
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 794b8c36c..96ba7609c 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
@@ -13,6 +13,10 @@ package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.event.events.BeforeImageUploadEvent
+import net.mamoe.mirai.event.events.ImageUploadEvent
+import net.mamoe.mirai.event.events.MessageSendEvent.FriendMessageSendEvent
+import net.mamoe.mirai.event.events.MessageSendEvent.GroupMessageSendEvent
 import net.mamoe.mirai.message.data.*
 import net.mamoe.mirai.utils.ExternalImage
 import net.mamoe.mirai.utils.WeakRefProperty
@@ -40,6 +44,9 @@ interface Contact : CoroutineScope {
 
     /**
      * 向这个对象发送消息.
+     *
+     * @see FriendMessageSendEvent 发送好友信息事件, cancellable
+     * @see GroupMessageSendEvent  发送群消息事件. cancellable
      */
     suspend fun sendMessage(message: MessageChain)
 
@@ -47,6 +54,9 @@ interface Contact : CoroutineScope {
      * 上传一个图片以备发送.
      * TODO: 群图片与好友图片之间是否通用还不确定.
      * TODO: 好友之间图片是否通用还不确定.
+     *
+     * @see BeforeImageUploadEvent 图片发送前事件, cancellable
+     * @see ImageUploadEvent 图片发送完成事件
      */
     suspend fun uploadImage(image: ExternalImage): Image
 
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 128b63a19..d4448a6ea 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
@@ -12,6 +12,7 @@
 package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
+import net.mamoe.mirai.event.events.*
 import net.mamoe.mirai.utils.MiraiExperimentalAPI
 
 /**
@@ -22,32 +23,41 @@ interface Group : Contact, CoroutineScope {
      * 群名称.
      *
      * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
+     * 频繁修改可能会被服务器拒绝.
      *
-     * 注: 频繁修改可能会被服务器拒绝.
+     * @see MemberPermissionChangeEvent
      */
     var name: String
     /**
      * 入群公告, 没有时为空字符串.
      *
      * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
+     *
+     * @see GroupEntranceAnnouncementChangeEvent
      */
-    var announcement: String
+    var entranceAnnouncement: String
     /**
      * 全体禁言状态. `true` 为开启.
      *
      * 当前仅能修改状态.
+     *
+     * @see GroupMuteAllEvent
      */// TODO: 2020/2/5 实现 muteAll 的查询
     var muteAll: Boolean
     /**
      * 坦白说状态. `true` 为允许.
      *
      * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
+
+     * @see GroupAllowConfessTalkEvent
      */
     var confessTalk: Boolean
     /**
      * 允许群员邀请好友入群的状态. `true` 为允许
      *
      * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
+     *
+     * @see GroupAllowMemberInviteEvent
      */
     var allowMemberInvite: Boolean
     /**
@@ -74,6 +84,8 @@ interface Group : Contact, CoroutineScope {
      * 机器人在这个群里的权限
      *
      * **MiraiExperimentalAPI**: 在未来可能会被修改
+     *
+     * @see BotGroupPermissionChangeEvent
      */
     @MiraiExperimentalAPI
     val botPermission: MemberPermission
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 92c663e2d..686217407 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
@@ -13,6 +13,9 @@ package net.mamoe.mirai.contact
 
 import net.mamoe.mirai.Bot
 import net.mamoe.mirai.event.events.MemberCardChangeEvent
+import net.mamoe.mirai.event.events.MemberLeaveEvent
+import net.mamoe.mirai.event.events.MemberMuteEvent
+import net.mamoe.mirai.event.events.MemberSpecialTitleChangeEvent
 import net.mamoe.mirai.utils.WeakRefProperty
 import kotlin.time.Duration
 import kotlin.time.ExperimentalTime
@@ -38,6 +41,7 @@ interface Member : QQ, Contact {
      * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      *
      * @see [groupCardOrNick] 获取非空群名片或昵称
+     *
      * @see MemberCardChangeEvent 群名片被管理员, 自己或 [Bot] 改动事件
      */
     var nameCard: String
@@ -55,7 +59,7 @@ interface Member : QQ, Contact {
      * 禁言
      *
      * @param durationSeconds 持续时间. 精确到秒. 范围区间表示为 `(0s, 30days]`. 超过范围则会抛出异常.
-     * @return 当机器人无权限禁言这个群成员时返回 `false`
+     * @return 机器人无权限时返回 `false`
      *
      * @see Int.minutesToSeconds
      * @see Int.hoursToSeconds
@@ -64,12 +68,16 @@ interface Member : QQ, Contact {
     suspend fun mute(durationSeconds: Int): Boolean
 
     /**
-     * 解除禁言. 在没有权限时会返回 `false`.
+     * 解除禁言. 机器人无权限时返回 `false`.
+     *
+     * @see MemberMuteEvent 成员被禁言事件.
      */
     suspend fun unmute(): Boolean
 
     /**
-     * 踢出该成员. 机器人无权限时返回 `false`
+     * 踢出该成员. 机器人无权限时返回 `false`.
+     *
+     * @see MemberLeaveEvent.Kick 成员被踢出事件.
      */
     suspend fun kick(message: String = ""): Boolean
 
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
index 1ea488a5e..986c00072 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/QQ.kt
@@ -16,6 +16,7 @@ import net.mamoe.mirai.Bot
 import net.mamoe.mirai.data.FriendNameRemark
 import net.mamoe.mirai.data.PreviousNameList
 import net.mamoe.mirai.data.Profile
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
 
 /**
  * QQ 对象.
@@ -49,6 +50,7 @@ interface QQ : Contact, CoroutineScope {
     /**
      * 查询用户资料
      */
+    @MiraiExperimentalAPI("还未支持")
     suspend fun queryProfile(): Profile
 
     /**
@@ -58,10 +60,12 @@ interface QQ : Contact, CoroutineScope {
      * - 昵称
      * - 共同群内的群名片
      */
+    @MiraiExperimentalAPI("还未支持")
     suspend fun queryPreviousNameList(): PreviousNameList
 
     /**
      * 查询机器人账号给这个人设置的备注
      */
+    @MiraiExperimentalAPI("还未支持")
     suspend fun queryRemark(): FriendNameRemark
 }
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
index 14d5b1106..0a8346cf4 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/Subscribable.kt
@@ -70,6 +70,9 @@ abstract class AbstractCancellableEvent : Event, CancellableEvent {
  */
 @UseExperimental(MiraiInternalAPI::class)
 suspend fun <E : Event> E.broadcast(): E = apply {
+    if (this is BroadcastControllable && !this.shouldBroadcast) {
+        return@apply
+    }
     if (this is BotEvent && !(this.bot as BotImpl<*>).onEvent(this)) {
         return@apply
     }
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
index 6350bb61a..f9607fd52 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/events/BotEvents.kt
@@ -10,9 +10,21 @@
 package net.mamoe.mirai.event.events
 
 import net.mamoe.mirai.Bot
-import net.mamoe.mirai.contact.Group
-import net.mamoe.mirai.contact.Member
-import net.mamoe.mirai.contact.MemberPermission
+import net.mamoe.mirai.contact.*
+import net.mamoe.mirai.event.AbstractCancellableEvent
+import net.mamoe.mirai.event.CancellableEvent
+import net.mamoe.mirai.message.data.Image
+import net.mamoe.mirai.message.data.MessageChain
+import net.mamoe.mirai.utils.ExternalImage
+
+
+@Suppress("unused")
+class EventCancelledException : RuntimeException {
+    constructor() : super()
+    constructor(message: String?) : super(message)
+    constructor(message: String?, cause: Throwable?) : super(message, cause)
+    constructor(cause: Throwable?) : super(cause)
+}
 
 
 // region Bot 状态
@@ -40,6 +52,64 @@ sealed class BotOfflineEvent : BotActiveEvent {
 
 // endregion
 
+// region 消息
+
+sealed class MessageSendEvent : BotEvent, BotActiveEvent, AbstractCancellableEvent() {
+    abstract val target: Contact
+    final override val bot: Bot
+        get() = target.bot
+
+    data class GroupMessageSendEvent(
+        override val target: Group,
+        var message: MessageChain
+    ) : MessageSendEvent(), CancellableEvent
+
+    data class FriendMessageSendEvent(
+        override val target: QQ,
+        var message: MessageChain
+    ) : MessageSendEvent(), CancellableEvent
+}
+
+// endregion
+
+// region 图片
+
+/**
+ * 图片上传前. 可以阻止上传
+ */
+data class BeforeImageUploadEvent(
+    val target: Contact,
+    val source: ExternalImage
+) : BotEvent, BotActiveEvent, AbstractCancellableEvent() {
+    override val bot: Bot
+        get() = target.bot
+}
+
+/**
+ * 图片上传完成
+ */
+sealed class ImageUploadEvent : BotEvent, BotActiveEvent, AbstractCancellableEvent() {
+    abstract val target: Contact
+    abstract val source: ExternalImage
+    override val bot: Bot
+        get() = target.bot
+
+    data class Succeed(
+        override val target: Contact,
+        override val source: ExternalImage,
+        val image: Image
+    ) : ImageUploadEvent(), CancellableEvent
+
+    data class Failed(
+        override val target: Contact,
+        override val source: ExternalImage,
+        val errno: Int,
+        val message: String
+    ) : ImageUploadEvent(), CancellableEvent
+}
+
+// endregion
+
 // region 群
 
 /**
@@ -64,8 +134,7 @@ interface GroupSettingChangeEvent<T> : GroupEvent, BotPassiveEvent {
 /**
  * 群名改变. 此事件广播前修改就已经完成.
  */
-sealed class GroupNameChangeEvent(
-) : GroupSettingChangeEvent<String>, BotPassiveEvent {
+sealed class GroupNameChangeEvent : GroupSettingChangeEvent<String>, BotPassiveEvent {
 
     /**
      * 由管理员操作
@@ -89,6 +158,34 @@ sealed class GroupNameChangeEvent(
     ) : GroupNameChangeEvent()
 }
 
+
+/**
+ * 入群公告改变. 此事件广播前修改就已经完成.
+ */
+sealed class GroupEntranceAnnouncementChangeEvent : GroupSettingChangeEvent<String>, BotPassiveEvent {
+
+    /**
+     * 由管理员操作
+     */
+    data class ByOperator(
+        override val origin: String,
+        override val new: String,
+        val operator: Member
+    ) : GroupEntranceAnnouncementChangeEvent() {
+        override val group: Group
+            get() = operator.group
+    }
+
+    /**
+     * 由机器人操作
+     */
+    data class ByBot(
+        override val origin: String,
+        override val new: String,
+        override val group: Group
+    ) : GroupEntranceAnnouncementChangeEvent()
+}
+
 /**
  * 群 "全员禁言" 功能状态改变. 此事件广播前修改就已经完成.
  */
@@ -118,8 +215,7 @@ sealed class GroupMuteAllEvent : GroupSettingChangeEvent<Boolean>, BotPassiveEve
 /**
  * 群 "坦白说" 功能状态改变. 此事件广播前修改就已经完成.
  */
-sealed class GroupConfessTalkEvent : GroupSettingChangeEvent<Boolean>, BotPassiveEvent {
-
+sealed class GroupAllowConfessTalkEvent : GroupSettingChangeEvent<Boolean>, BotPassiveEvent {
     /**
      * 由管理员操作
      */
@@ -127,7 +223,7 @@ sealed class GroupConfessTalkEvent : GroupSettingChangeEvent<Boolean>, BotPassiv
         override val origin: Boolean,
         override val new: Boolean,
         val operator: Member
-    ) : GroupConfessTalkEvent() {
+    ) : GroupAllowConfessTalkEvent() {
         override val group: Group
             get() = operator.group
     }
@@ -139,7 +235,33 @@ sealed class GroupConfessTalkEvent : GroupSettingChangeEvent<Boolean>, BotPassiv
         override val origin: Boolean,
         override val new: Boolean,
         override val group: Group
-    ) : GroupConfessTalkEvent()
+    ) : GroupAllowConfessTalkEvent()
+}
+
+/**
+ * 群 "允许群员邀请好友加群" 功能状态改变. 此事件广播前修改就已经完成.
+ */
+sealed class GroupAllowMemberInviteEvent : GroupSettingChangeEvent<Boolean>, BotPassiveEvent {
+    /**
+     * 由管理员操作
+     */
+    data class ByOperator(
+        override val origin: Boolean,
+        override val new: Boolean,
+        val operator: Member
+    ) : GroupAllowMemberInviteEvent() {
+        override val group: Group
+            get() = operator.group
+    }
+
+    /**
+     * 由机器人操作
+     */
+    data class ByBot(
+        override val origin: Boolean,
+        override val new: Boolean,
+        override val group: Group
+    ) : GroupAllowMemberInviteEvent()
 }
 
 // endregion
@@ -157,11 +279,11 @@ data class MemberJoinEvent(override val member: Member) : GroupMemberEvent, BotP
 /**
  * 成员离开群的事件
  */
-sealed class MemberLeftEvent : GroupMemberEvent {
+sealed class MemberLeaveEvent : GroupMemberEvent {
     /**
      * 成员被踢出群. 成员不可能是机器人自己.
      */
-    sealed class Kick : MemberLeftEvent() {
+    sealed class Kick : MemberLeaveEvent() {
         /**
          * 被管理员踢出
          */
@@ -181,7 +303,7 @@ sealed class MemberLeftEvent : GroupMemberEvent {
     /**
      * 成员主动离开
      */
-    data class Quit(override val member: Member) : MemberLeftEvent()
+    data class Quit(override val member: Member) : MemberLeaveEvent()
 }
 
 // endregion
diff --git a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
index 69e6b3dcf..bd15503f9 100644
--- a/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
+++ b/mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman/Main.kt
@@ -81,14 +81,6 @@ suspend fun main() {
 
         "你好" reply "你好!"
 
-        startsWith("profile", removePrefix = true) {
-            val account = it.trim()
-            if (account.isNotEmpty()) {
-                bot.getFriend(account.toLong())
-            } else {
-                sender
-            }.queryProfile().toString().reply()
-        }
         "grouplist" reply {
 
             //"https://ssl.ptlogin2.qq.com/jump?pt_clientver=5509&pt_src=1&keyindex=9&clientuin=" + bot.qqAccount + "&clientkey=" + com.tick_tock.pctim.utils.Util.byte2HexString(
diff --git a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
index 5d52aa1b5..812299984 100644
--- a/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
+++ b/mirai-japt/src/main/kotlin/net/mamoe/mirai/japt/internal/BlockingContactsImpl.kt
@@ -52,7 +52,7 @@ internal class BlockingGroupImpl(private val delegate: Group) : BlockingGroup {
     override fun toFullString(): String = delegate.toFullString()
     override fun getMember(id: Long): BlockingMember = delegate[id].blocking()
     override fun getBot(): BlockingBot = delegate.bot.blocking()
-    override fun getAnnouncement(): String = delegate.announcement
+    override fun getAnnouncement(): String = delegate.entranceAnnouncement
     @UseExperimental(MiraiInternalAPI::class)
     override fun getMembers(): Map<Long, BlockingMember> =
         delegate.members.delegate.toList().associateBy { it.id }.mapValues { it.value.blocking() }