diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
index 58b5e155d..478466622 100644
--- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
+++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribers.kt
@@ -3,6 +3,9 @@
 package net.mamoe.mirai.event
 
 import net.mamoe.mirai.Bot
+import net.mamoe.mirai.contact.isAdministrator
+import net.mamoe.mirai.contact.isOperator
+import net.mamoe.mirai.contact.isOwner
 import net.mamoe.mirai.message.Message
 import net.mamoe.mirai.message.any
 import net.mamoe.mirai.network.protocol.tim.packet.event.FriendMessage
@@ -18,12 +21,12 @@ import kotlin.jvm.JvmName
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*>>.() -> Unit) {
+suspend inline fun subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
-    MessageSubscribersBuilder<MessagePacket<*>> { listener ->
-        subscribeAlways<MessagePacket<*>> {
+    MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
+        subscribeAlways<MessagePacket<*, *>> {
             listener(it)
         }
     }.apply { listeners() }
@@ -66,12 +69,12 @@ suspend inline fun subscribeFriendMessages(crossinline listeners: suspend Messag
  */
 @UseExperimental(ExperimentalContracts::class)
 @MessageDsl
-suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*>>.() -> Unit) {
+suspend inline fun Bot.subscribeMessages(crossinline listeners: suspend MessageSubscribersBuilder<MessagePacket<*, *>>.() -> Unit) {
     contract {
         callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
     }
-    MessageSubscribersBuilder<MessagePacket<*>> { listener ->
-        this.subscribeAlways<MessagePacket<*>> {
+    MessageSubscribersBuilder<MessagePacket<*, *>> { listener ->
+        this.subscribeAlways<MessagePacket<*, *>> {
             listener(it)
         }
     }.apply { listeners() }
@@ -111,11 +114,11 @@ suspend inline fun Bot.subscribeFriendMessages(crossinline listeners: suspend Me
 
 private typealias AnyReplier<T> = @MessageDsl suspend T.(String) -> Any?
 
-private suspend inline operator fun <T : MessagePacket<*>> (@MessageDsl suspend T.(String) -> Unit).invoke(t: T) =
+private suspend inline operator fun <T : MessagePacket<*, *>> (@MessageDsl suspend T.(String) -> Unit).invoke(t: T) =
     this.invoke(t, t.message.stringValue)
 
 @JvmName("invoke1") //Avoid Platform declaration clash
-private suspend inline operator fun <T : MessagePacket<*>> AnyReplier<T>.invoke(t: T): Any? =
+private suspend inline operator fun <T : MessagePacket<*, *>> AnyReplier<T>.invoke(t: T): Any? =
     this.invoke(t, t.message.stringValue)
 
 /**
@@ -127,7 +130,7 @@ private suspend inline operator fun <T : MessagePacket<*>> AnyReplier<T>.invoke(
 // TODO: 2019/11/29 应定义为 inline, 但这会导致一个 JVM run-time VerifyError. 等待 kotlin 修复 bug
 @Suppress("unused")
 @MessageDsl
-class MessageSubscribersBuilder<T : MessagePacket<*>>(
+class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
     inline val subscriber: suspend (@MessageDsl suspend T.(String) -> Unit) -> Unit
 ) {
     /**
@@ -194,6 +197,27 @@ class MessageSubscribersBuilder<T : MessagePacket<*>>(
     @MessageDsl
     suspend inline fun sentBy(qqId: Long, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) = sentBy(qqId.toUInt(), onEvent)
 
+    /**
+     * 如果是管理员或群主发的消息, 就执行 [onEvent]
+     */
+    @MessageDsl
+    suspend inline fun sentByOperator(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+        content({ this is GroupMessage && sender.permission.isOperator() }, onEvent)
+
+    /**
+     * 如果是管理员发的消息, 就执行 [onEvent]
+     */
+    @MessageDsl
+    suspend inline fun sentByAdministrator(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+        content({ this is GroupMessage && sender.permission.isAdministrator() }, onEvent)
+
+    /**
+     * 如果是群主发的消息, 就执行 [onEvent]
+     */
+    @MessageDsl
+    suspend inline fun sentByOwner(noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
+        content({ this is GroupMessage && sender.permission.isOwner() }, onEvent)
+
     /**
      * 如果是来自这个群的消息, 就执行 [onEvent]
      */
@@ -221,21 +245,74 @@ class MessageSubscribersBuilder<T : MessagePacket<*>>(
     suspend inline fun content(noinline filter: T.(String) -> Boolean, noinline onEvent: @MessageDsl suspend T.(String) -> Unit) =
         subscriber { if (this.filter(message.stringValue)) onEvent(this) }
 
+    /**
+     * 若消息内容包含 [this] 则回复 [reply]
+     */
     @MessageDsl
-    suspend inline infix fun String.containsReply(replier: String) =
-        content({ this@containsReply in it }) { this@content.reply(replier) }
+    suspend inline infix fun String.containsReply(reply: String) =
+        content({ this@containsReply in it }) { this@content.reply(reply) }
 
+    /**
+     * 若消息内容包含 [this] 则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是消息内容 string.
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
     @MessageDsl
     suspend inline infix fun String.containsReply(noinline replier: AnyReplier<T>) =
-        content({ this@containsReply in it }) { replier(this) }
+        content({ this@containsReply in it }) {
+            @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
+            executeAndReply(replier)
+        }
 
+    /**
+     * 不考虑空格, 若消息内容以 [this] 开始则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是去掉用来判断的前缀并删除前后空格后的字符串.
+     * 如当消息为 "kick    123456     " 时
+     * ```kotlin
+     * "kick" startsWithReply {
+     *     println(it) // it 为 "123456"
+     * }
+     * ```
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
+     */
     @MessageDsl
-    suspend inline infix fun String.startsWithReply(noinline replier: AnyReplier<T>) =
-        content({ it.startsWith(this@startsWithReply) }) { replier(this) }
+    suspend inline infix fun String.startsWithReply(noinline replier: AnyReplier<T>) {
+        val toCheck = this.trimStart()
+        content({ it.trimStart().startsWith(toCheck) }) {
+            @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
+            executeAndReply {
+                replier(it.removePrefix(toCheck).trim())
+            }
+        }
+    }
 
+    /**
+     * 不考虑空格, 若消息内容以 [this] 结尾则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是去掉用来判断的后缀并删除前后空格后的字符串.
+     * 如当消息为 "  123456 test" 时
+     * ```kotlin
+     * "test" endswithReply {
+     *     println(it) // it 为 "123456"
+     * }
+     * ```
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
     @MessageDsl
-    suspend inline infix fun String.endswithReply(noinline replier: AnyReplier<T>) =
-        content({ it.endsWith(this@endswithReply) }) { replier(this) }
+    suspend inline infix fun String.endswithReply(noinline replier: AnyReplier<T>) {
+        val toCheck = this.trimEnd()
+        content({ it.endsWith(this@endswithReply) }) {
+            @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
+            executeAndReply {
+                replier(it.removeSuffix(toCheck).trim())
+            }
+        }
+    }
 
     @MessageDsl
     suspend inline infix fun String.reply(reply: String) = case(this) {
@@ -243,17 +320,23 @@ class MessageSubscribersBuilder<T : MessagePacket<*>>(
     }
 
     @MessageDsl
-    suspend inline infix fun String.reply(noinline reply: AnyReplier<T>) = case(this) {
-        when (val message = reply(this)) {
-            is Message -> reply(message)
+    suspend inline infix fun String.reply(noinline replier: AnyReplier<T>) = case(this) {
+        @Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
+        executeAndReply(replier)
+    }
+
+    @PublishedApi
+    @Suppress("NOTHING_TO_INLINE")
+    internal suspend inline fun T.executeAndReply(noinline replier: AnyReplier<T>) {
+        when (val message = replier(this)) {
+            is Message -> this.reply(message)
             is Unit -> {
 
             }
-            else -> reply(message.toString())
+            else -> this.reply(message.toString())
         }
     }
 
-
 /* 易产生迷惑感
     suspend inline fun replyCase(equals: String, trim: Boolean = true, noinline replier: MessageReplier<T>) = case(equals, trim) { reply(replier(this)) }
     suspend inline fun replyContains(value: String, noinline replier: MessageReplier<T>) = content({ value in it }) { replier(this) }
@@ -265,6 +348,7 @@ class MessageSubscribersBuilder<T : MessagePacket<*>>(
 /**
  * DSL 标记. 将能让 IDE 阻止一些错误的方法调用.
  */
+@Retention(AnnotationRetention.SOURCE)
 @Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
 @DslMarker
 internal annotation class MessageDsl
\ No newline at end of file
diff --git a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
index 431e3f048..df6142978 100644
--- a/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
+++ b/mirai-demos/mirai-demo-1/src/main/java/demo/subscribe/SubscribeSamples.kt
@@ -118,7 +118,7 @@ suspend fun Bot.messageDSL() {
 
 
         // 如果是这个 QQ 号发送的消息(可以是好友消息也可以是群消息)
-        sentBy(1040400290) {
+        sentBy(123456789) {
         }