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 46174e03f..8762e30b5 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Group.kt
@@ -3,7 +3,7 @@
 package net.mamoe.mirai.contact
 
 import kotlinx.coroutines.CoroutineScope
-
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
 
 /**
  * 群. 在 QQ Android 中叫做 "Troop"
@@ -12,15 +12,15 @@ interface Group : Contact, CoroutineScope {
     /**
      * 群名称.
      *
-     * 在修改时将会异步上传至服务器.
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      *
-     * 注: 频繁修改可能会被服务器拒绝
+     * 注: 频繁修改可能会被服务器拒绝.
      */
     var name: String
     /**
      * 入群公告, 没有时为空字符串.
      *
-     * 在修改时将会异步上传至服务器.
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      */
     var announcement: String
     /**
@@ -32,11 +32,13 @@ interface Group : Contact, CoroutineScope {
     /**
      * 坦白说状态. `true` 为允许.
      *
-     * 在修改时将会异步上传至服务器.
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      */
     var confessTalk: Boolean
     /**
      * 允许群员邀请好友入群的状态. `true` 为允许
+     *
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      */
     var allowMemberInvite: Boolean
     /**
@@ -45,14 +47,9 @@ interface Group : Contact, CoroutineScope {
     val autoApprove: Boolean
     /**
      * 匿名聊天
-     * [可查已完成]
      */
     val anonymousChat: Boolean
 
-    /**
-     * ====以上字段在更新值的时候会自动异步上报服务器更改群信息====
-     */
-
     /**
      * 同为 groupCode, 用户看到的群号码.
      */
@@ -64,16 +61,23 @@ interface Group : Contact, CoroutineScope {
     val owner: Member
 
 
+    /**
+     * 机器人在这个群里的权限
+     *
+     * **MiraiExperimentalAPI**: 在未来可能会被修改
+     */
+    @MiraiExperimentalAPI
     val botPermission: MemberPermission
 
 
     /**
+     * 群成员列表, 不含机器人自己, 含群主.
      * 在 [Group] 实例创建的时候查询一次. 并与事件同步事件更新
      */
     val members: ContactList<Member>
 
     /**
-     * 获取群成员实例. 若此 id 的成员不存在, 则会抛出 [kotlin.NoSuchElementException]
+     * 获取群成员实例. 不存在时抛出 [kotlin.NoSuchElementException]
      */
     operator fun get(id: Long): Member
 
@@ -93,5 +97,6 @@ interface Group : Contact, CoroutineScope {
     suspend fun quit(): Boolean
 
 
+    @MiraiExperimentalAPI
     fun toFullString(): String = "Group(id=${this.id}, name=$name, owner=${owner.id}, members=${members.idContentString})"
 }
\ No newline at end of file
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 614c93899..684935dfc 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
@@ -21,24 +21,20 @@ interface Member : QQ, Contact {
      */
     val permission: MemberPermission
 
-    /**
-     * ====以下字段会在更新后异步更新到服务器====
-     */
-
     /**
      * 群名片
+     *
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      */
     var groupCard: String
 
     /**
      * 群头衔
+     *
+     * 在修改时将会异步上传至服务器. 无权限修改时将会抛出异常 [PermissionDeniedException]
      */
     var specialTitle: String
 
-    /**
-     * ====以上字段会在更新后异步更新到服务器====
-     */
-
     /**
      * 禁言
      *
@@ -73,59 +69,4 @@ suspend inline fun Member.mute(duration: Duration): Boolean {
 suspend inline fun Member.mute(durationSeconds: UInt): Boolean {
     require(durationSeconds.toInt() <= 30 * 24 * 3600) { "duration must be at most 1 month" }
     return this.mute(durationSeconds.toInt()) // same bin rep.
-}
-
-/**
- * 群成员的权限
- */
-enum class MemberPermission {
-    /**
-     * 群主
-     */
-    OWNER,
-    /**
-     * 管理员
-     */
-    ADMINISTRATOR,
-    /**
-     * 一般群成员
-     */
-    MEMBER;
-}
-
-/**
- * 是群主
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
-
-/**
- * 是管理员
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
-
-/**
- * 是管理员或群主
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
-
-
-/**
- * 是群主
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun Member.isOwner(): Boolean = this.permission.isOwner()
-
-/**
- * 是管理员
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
-
-/**
- * 时管理员或群主
- */
-@Suppress("NOTHING_TO_INLINE")
-inline fun Member.isOperator(): Boolean = this.permission.isOperator()
\ No newline at end of file
+}
\ No newline at end of file
diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt
new file mode 100644
index 000000000..09fc3148d
--- /dev/null
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Permission.kt
@@ -0,0 +1,92 @@
+package net.mamoe.mirai.contact
+
+import net.mamoe.mirai.utils.MiraiExperimentalAPI
+
+
+/**
+ * 群成员的权限
+ */
+enum class MemberPermission {
+    /**
+     * 群主
+     */
+    OWNER,
+    /**
+     * 管理员
+     */
+    ADMINISTRATOR,
+    /**
+     * 一般群成员
+     */
+    MEMBER;
+}
+
+/**
+ * 是群主
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun MemberPermission.isOwner(): Boolean = this == MemberPermission.OWNER
+
+/**
+ * 是管理员
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun MemberPermission.isAdministrator(): Boolean = this == MemberPermission.ADMINISTRATOR
+
+/**
+ * 是管理员或群主
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun MemberPermission.isOperator(): Boolean = isAdministrator() || isOwner()
+
+
+/**
+ * 是群主
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun Member.isOwner(): Boolean = this.permission.isOwner()
+
+/**
+ * 是管理员
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun Member.isAdministrator(): Boolean = this.permission.isAdministrator()
+
+/**
+ * 时管理员或群主
+ */
+@Suppress("NOTHING_TO_INLINE")
+inline fun Member.isOperator(): Boolean = this.permission.isOperator()
+
+
+
+/**
+ * 权限不足
+ */
+class PermissionDeniedException : IllegalStateException {
+    constructor() : super("Permission denied")
+    constructor(message: String?) : super(message)
+}
+
+@UseExperimental(MiraiExperimentalAPI::class)
+inline fun Group.checkBotPermission(
+    required: MemberPermission,
+    lazyMessage: () -> String = {
+        "Permission denied: required $required, got actual $botPermission for $bot in group $id"
+    }
+) {
+    if (botPermission != required) {
+        throw PermissionDeniedException(lazyMessage())
+    }
+}
+
+@UseExperimental(MiraiExperimentalAPI::class)
+inline fun Group.checkBotPermissionOperator(
+    lazyMessage: () -> String = {
+        "Permission denied: required ${MemberPermission.ADMINISTRATOR} or ${MemberPermission.OWNER}, got actual $botPermission for $bot in group $id"
+    }
+) {
+    if (!botPermission.isOperator()) {
+        throw PermissionDeniedException(lazyMessage())
+    }
+}
\ No newline at end of file