Explorar el Código

Rearrange `MessageSubscribersBuilder`

Him188 hace 5 años
padre
commit
a5b82c5e48

+ 508 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt

@@ -0,0 +1,508 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+@file:Suppress(
+    "unused", "DSL_SCOPE_VIOLATION_WARNING", "INAPPLICABLE_JVM_NAME", "INVALID_CHARACTERS",
+    "NAME_CONTAINS_ILLEGAL_CHARS", "FunctionName"
+)
+
+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.event.internal.*
+import net.mamoe.mirai.message.ContactMessage
+import net.mamoe.mirai.message.FriendMessage
+import net.mamoe.mirai.message.GroupMessage
+import net.mamoe.mirai.message.TempMessage
+import net.mamoe.mirai.message.data.At
+import net.mamoe.mirai.message.data.Message
+import net.mamoe.mirai.message.data.firstIsInstance
+import net.mamoe.mirai.message.data.firstIsInstanceOrNull
+import net.mamoe.mirai.utils.PlannedRemoval
+import kotlin.js.JsName
+import kotlin.jvm.JvmName
+import kotlin.jvm.JvmOverloads
+import kotlin.jvm.JvmSynthetic
+
+
+/**
+ * 消息事件的处理器.
+ *
+ * 注:
+ * 接受者 T 为 [ContactMessage]
+ * 参数 String 为 转为字符串了的消息 ([Message.toString])
+ */
+typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
+
+
+/**
+ * 消息订阅构造器
+ *
+ * @param M 消息类型
+ * @param R 消息监听器内部的返回值
+ * @param Ret 每个 DSL 函数创建监听器之后的返回值
+ *
+ * @see subscribeFriendMessages
+ */
+@MessageDsl
+open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
+    /**
+     * 用于 [MessageListener] 无返回值的替代.
+     */
+    @PublishedApi
+    internal val stub: RR,
+    /**
+     * invoke 这个 lambda 时, 它将会把 [消息事件的处理器][MessageListener] 注册给事件, 并返回注册完成返回的监听器.
+     */
+    val subscriber: (M.(String) -> Boolean, MessageListener<M, RR>) -> Ret
+) {
+    @Suppress("DEPRECATION_ERROR")
+    open fun newListeningFilter(filter: M.(String) -> Boolean): ListeningFilter = ListeningFilter(filter)
+
+    /**
+     * 由 [contains], [startsWith] 等 DSL 创建出的监听条件, 使用 [invoke] 将其注册给事件
+     */
+    open inner class ListeningFilter @Deprecated( // keep it for development warning
+        "use newListeningFilter instead",
+        ReplaceWith("newListeningFilter(filter)"),
+        level = DeprecationLevel.ERROR
+    ) constructor(
+        val filter: M.(String) -> Boolean
+    ) {
+        /** 进行逻辑 `or`. */
+        infix fun or(another: ListeningFilter): ListeningFilter =
+            newListeningFilter { filter.invoke(this, it) || another.filter.invoke(this, it) }
+
+        /** 进行逻辑 `and`. */
+        infix fun and(another: ListeningFilter): ListeningFilter =
+            newListeningFilter { filter.invoke(this, it) && another.filter.invoke(this, it) }
+
+        /** 进行逻辑 `xor`. */
+        infix fun xor(another: ListeningFilter): ListeningFilter =
+            newListeningFilter { filter.invoke(this, it) xor another.filter.invoke(this, it) }
+
+        /** 进行逻辑 `nand`, 即 `not and`. */
+        infix fun nand(another: ListeningFilter): ListeningFilter =
+            newListeningFilter { !filter.invoke(this, it) || !another.filter.invoke(this, it) }
+
+        /** 进行逻辑 `not` */
+        fun not(): ListeningFilter = newListeningFilter { !filter.invoke(this, it) }
+
+        /** 启动事件监听. */
+        // do not inline due to kotlin (1.3.61) bug: java.lang.IllegalAccessError
+        operator fun invoke(onEvent: MessageListener<M, R>): Ret = content(filter, onEvent)
+    }
+
+    /** 启动这个监听器, 在满足条件时回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.reply(toReply: String): Ret = content(filter) { reply(toReply);stub }
+
+
+    /** 启动这个监听器, 在满足条件时回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.reply(message: Message): Ret = content(filter) { reply(message);stub }
+
+    /** 启动这个监听器, 在满足条件时回复原消息 */
+    @JvmName("reply3")
+    @MessageDsl
+    open infix fun ListeningFilter.`->`(toReply: String): Ret = this.reply(toReply)
+
+    /** 启动这个监听器, 在满足条件时回复原消息 */
+    @JvmName("reply3")
+    @MessageDsl
+    open infix fun ListeningFilter.`->`(message: Message): Ret = this.reply(message)
+
+    /** 启动这个监听器, 在满足条件时回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.reply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret =
+        content(filter) { executeAndReply(this, replier) }
+
+    /** 启动这个监听器, 在满足条件时引用回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.quoteReply(toReply: String): Ret =
+        content(filter) { quoteReply(toReply);stub }
+
+    /** 启动这个监听器, 在满足条件时引用回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.quoteReply(toReply: Message): Ret =
+        content(filter) { quoteReply(toReply);stub }
+
+    /** 启动这个监听器, 在满足条件时执行 [replier] 并引用回复原消息 */
+    @MessageDsl
+    open infix fun ListeningFilter.quoteReply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret =
+        content(filter) { executeAndQuoteReply(this, replier) }
+
+    /** 无任何触发条件, 每次收到消息都执行 [onEvent] */
+    @MessageDsl
+    open fun always(onEvent: MessageListener<M, RR>): Ret = subscriber({ true }, onEvent)
+
+    /** [消息内容][Message.contentToString] `==` [equals] */
+    @MessageDsl
+    fun case(equals: String, ignoreCase: Boolean = false, trim: Boolean = true): ListeningFilter =
+        caseImpl(equals, ignoreCase, trim)
+
+    /** 如果[消息内容][Message.contentToString]  `==` [equals] */
+    @MessageDsl
+    operator fun String.invoke(block: MessageListener<M, R>): Ret = case(this, onEvent = block)
+
+    /** 如果[消息内容][Message.contentToString]  [matches] */
+    @MessageDsl
+    @JvmSynthetic
+    @JvmName("matchingExtension")
+    infix fun Regex.matching(block: MessageListener<M, R>): Ret = content({ it matches this@matching }, block)
+
+    /** 如果[消息内容][Message.contentToString] [Regex.find] 不为空 */
+    @MessageDsl
+    @JvmSynthetic
+    @JvmName("findingExtension")
+    infix fun Regex.finding(block: @MessageDsl suspend M.(MatchResult) -> R): Ret =
+        always { content -> [email protected](content)?.let { block(this, it) } ?: stub }
+
+    /**
+     * [消息内容][Message.contentToString] `==` [equals]
+     * @param trim `true` 则删除首尾空格后比较
+     * @param ignoreCase `true` 则不区分大小写
+     */
+    @MessageDsl
+    fun case(
+        equals: String, ignoreCase: Boolean = false, trim: Boolean = true,
+        onEvent: MessageListener<M, R>
+    ): Ret = (if (trim) equals.trim() else equals).let { toCheck ->
+        content({ (if (trim) it.trim() else it).equals(toCheck, ignoreCase = ignoreCase) }) {
+            onEvent(this, this.message.contentToString())
+        }
+    }
+
+    /** [消息内容][Message.contentToString]包含 [sub] */
+    @MessageDsl
+    fun contains(sub: String): ListeningFilter = content { sub in it }
+
+    /**
+     * [消息内容][Message.contentToString]包含 [sub] 中的任意一个元素
+     */
+    @MessageDsl
+    fun contains(
+        sub: String, ignoreCase: Boolean = false, trim: Boolean = true,
+        onEvent: MessageListener<M, R>
+    ): Ret = containsImpl(sub, ignoreCase, trim, onEvent)
+
+    /** [消息内容][Message.contentToString]包含 [sub] */
+    @JvmOverloads
+    @MessageDsl
+    fun containsAny(vararg sub: String, ignoreCase: Boolean = false, trim: Boolean = true): ListeningFilter =
+        containsAnyImpl(*sub, ignoreCase = ignoreCase, trim = trim)
+
+    /** [消息内容][Message.contentToString]包含 [sub] */
+    @JvmOverloads
+    @MessageDsl
+    fun containsAll(vararg sub: String, ignoreCase: Boolean = false, trim: Boolean = true): ListeningFilter =
+        containsAllImpl(sub, ignoreCase = ignoreCase, trim = trim)
+
+
+    /** 如果消息的前缀是 [prefix] */
+    @MessageDsl
+    fun startsWith(prefix: String, trim: Boolean = true): ListeningFilter {
+        val toCheck = if (trim) prefix.trim() else prefix
+        return content { (if (trim) it.trim() else it).startsWith(toCheck) }
+    }
+
+    /** 如果消息的前缀是 [prefix] */
+    @MessageDsl
+    fun startsWith(
+        prefix: String, removePrefix: Boolean = true, trim: Boolean = true,
+        onEvent: @MessageDsl suspend M.(String) -> R
+    ): Ret = startsWithImpl(prefix, removePrefix, trim, onEvent)
+
+    /** 如果消息的结尾是 [suffix] */
+    @MessageDsl
+    @JvmOverloads // for binary compatibility
+    fun endsWith(suffix: String, trim: Boolean = true): ListeningFilter =
+        content { if (trim) it.trimEnd().endsWith(suffix) else it.endsWith(suffix) }
+
+    /** 如果消息的结尾是 [suffix] */
+    @MessageDsl
+    fun endsWith(
+        suffix: String, removeSuffix: Boolean = true, trim: Boolean = true,
+        onEvent: @MessageDsl suspend M.(String) -> R
+    ): Ret = endsWithImpl(suffix, removeSuffix, trim, onEvent)
+
+    /** 如果是这个人发的消息. 消息目前只会是群消息 */
+    @MessageDsl
+    fun sentBy(name: String): ListeningFilter = content { this is GroupMessage && this.senderName == name }
+
+    /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
+    @MessageDsl
+    fun sentBy(qq: Long): ListeningFilter = content { sender.id == qq }
+
+    /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
+    @MessageDsl
+    fun sentBy(qq: Long, onEvent: MessageListener<M, R>): Ret = content { this.sender.id == qq }.invoke(onEvent)
+
+    /** 如果是好友发来的消息 */
+    @MessageDsl
+    fun sentByFriend(onEvent: MessageListener<FriendMessage, R>): Ret =
+        content({ this is FriendMessage }) { onEvent(this as FriendMessage, it) }
+
+    /** 如果是好友发来的消息 */
+    @MessageDsl
+    fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage }
+
+    /** 如果是好友发来的消息 */
+    @MessageDsl
+    fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessage }
+
+    /** 如果是管理员或群主发的消息 */
+    @MessageDsl
+    fun sentByOperator(): ListeningFilter = content { this is GroupMessage && sender.permission.isOperator() }
+
+    /** 如果是管理员发的消息 */
+    @MessageDsl
+    fun sentByAdministrator(): ListeningFilter = content { this is GroupMessage && sender.permission.isAdministrator() }
+
+    /** 如果是群主发的消息 */
+    @MessageDsl
+    fun sentByOwner(): ListeningFilter = content { this is GroupMessage && sender.isOwner() }
+
+    /** 如果是来自这个群的消息 */
+    @MessageDsl
+    fun sentFrom(groupId: Long): ListeningFilter = content { this is GroupMessage && group.id == groupId }
+
+    /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At] */
+    @MessageDsl
+    fun atBot(): ListeningFilter = content { message.firstIsInstanceOrNull<At>()?.target == bot.id }
+
+    /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At], 就执行 [onEvent] */
+    @MessageDsl
+    fun atBot(onEvent: @MessageDsl suspend M.(String) -> R): Ret =
+        content { message.firstIsInstanceOrNull<At>()?.target == bot.id }.invoke {
+            onEvent.invoke(this, message.contentToString())
+        }
+
+    @MessageDsl
+    inline fun <reified N : Message> has(noinline onEvent: @MessageDsl suspend M.(N) -> R): Ret =
+        content { message.any { it is N } }.invoke { onEvent.invoke(this, message.firstIsInstance()) }
+
+    /** [消息内容][Message.contentToString]包含 [N] 类型的 [Message] */
+    @MessageDsl
+    inline fun <reified N : Message> has(): ListeningFilter = content { message.any { it is N } }
+
+    /** 如果 [mapper] 返回值非空, 就执行 [onEvent] */
+    @MessageDsl
+    open fun <N : Any> mapping(mapper: M.(String) -> N?, onEvent: @MessageDsl suspend M.(N) -> R): Ret =
+        always { onEvent.invoke(this, mapper(this, message.contentToString()) ?: return@always stub) }
+
+    /** 如果 [filter] 返回 `true` */
+    @MessageDsl
+    fun content(filter: M.(String) -> Boolean): ListeningFilter = newListeningFilter(filter)
+
+    /** [消息内容][Message.contentToString]可由正则表达式匹配([Regex.matchEntire]) */
+    @MessageDsl
+    fun matching(regex: Regex): ListeningFilter = content { regex.matchEntire(it) != null }
+
+
+    /** [消息内容][Message.contentToString]可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent` */
+    @MessageDsl
+    fun matching(regex: Regex, onEvent: @MessageDsl suspend M.(MatchResult) -> Unit): Ret =
+        always { executeAndReply(this) { onEvent.invoke(this, regex.matchEntire(it) ?: return@always stub) } }
+
+    /** [消息内容][Message.contentToString]可由正则表达式查找([Regex.find]) */
+    @MessageDsl
+    fun finding(regex: Regex): ListeningFilter = content { regex.find(it) != null }
+
+    /** [消息内容][Message.contentToString]可由正则表达式查找([Regex.find]), 就执行 `onEvent` */
+    @MessageDsl
+    fun finding(regex: Regex, onEvent: @MessageDsl suspend M.(MatchResult) -> Unit): Ret =
+        always { executeAndReply(this) { onEvent.invoke(this, regex.find(it) ?: return@always stub) } }
+
+
+    /** [消息内容][Message.contentToString]包含 [this] 则回复 [reply] */
+    @MessageDsl
+    open infix fun String.containsReply(reply: String): Ret =
+        content({ this@containsReply in it }, { reply(reply); stub })
+
+    /**
+     * [消息内容][Message.contentToString]包含 [this] 则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是消息内容 string.
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
+    @MessageDsl
+    open infix fun String.containsReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret =
+        content({ this@containsReply in it }, { executeAndReply(this, replier) })
+
+    /**
+     * [消息内容][Message.contentToString]可由正则表达式匹配([Regex.matchEntire]), 则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是消息内容 string.
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
+    @MessageDsl
+    open infix fun Regex.matchingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
+        always { executeAndReply(this) { replier.invoke(this, matchEntire(it) ?: return@always stub) } }
+
+    /**
+     * [消息内容][Message.contentToString]可由正则表达式查找([Regex.find]), 则执行 [replier] 并将其返回值回复给发信对象.
+     *
+     * [replier] 的 `it` 将会是消息内容 string.
+     *
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
+    @MessageDsl
+    open infix fun Regex.findingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
+        always { executeAndReply(this) { replier.invoke(this, [email protected](it) ?: return@always stub) } }
+
+
+    /**
+     * 不考虑空格, [消息内容][Message.contentToString]以 [this] 结尾则执行 [replier] 并将其返回值回复给发信对象.
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
+     */
+    @MessageDsl
+    open infix fun String.endsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
+        val toCheck = this.trimEnd()
+        return content({ it.trim().endsWith(toCheck) }, {
+            executeAndReply(this) { replier(this, it.trim().removeSuffix(toCheck)) }
+        })
+    }
+
+    /** 当发送的消息内容为 [this] 就回复 [reply] */
+    @MessageDsl
+    open infix fun String.reply(reply: String): Ret {
+        val toCheck = this.trim()
+        return content({ it.trim() == toCheck }, { reply(reply);stub })
+    }
+
+    /** 当发送的消息内容为 [this] 就回复 [reply] */
+    @MessageDsl
+    open infix fun String.reply(reply: Message): Ret {
+        val toCheck = this.trim()
+        return content({ it.trim() == toCheck }, { reply(reply);stub })
+    }
+
+    /** 当发送的消息内容为 [this] 就执行并回复 [replier] 的返回值 */
+    @MessageDsl
+    open infix fun String.reply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
+        val toCheck = this.trim()
+        return content({ it.trim() == toCheck }, {
+            @Suppress("DSL_SCOPE_VIOLATION_WARNING")
+            executeAndReply(this) { replier(this, it.trim()) }
+        })
+    }
+
+    /////////////////////////////////
+    //// DEPRECATED AND INTERNAL ////
+    /////////////////////////////////
+
+    @PublishedApi
+    @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE", "UNCHECKED_CAST") // false positive
+    internal suspend inline fun executeAndReply(m: M, replier: suspend M.(String) -> Any?): RR {
+        when (val message = replier(m, m.message.contentToString())) {
+            is Message -> m.reply(message)
+            is Unit -> Unit
+            else -> m.reply(message.toString())
+        }
+        return stub
+    }
+
+    @PublishedApi
+    @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE", "UNCHECKED_CAST") // false positive
+    internal suspend inline fun executeAndQuoteReply(m: M, replier: suspend M.(String) -> Any?): RR {
+        when (val message = replier(m, m.message.contentToString())) {
+            is Message -> m.quoteReply(message)
+            is Unit -> Unit
+            else -> m.quoteReply(message.toString())
+        }
+        return stub
+    }
+
+    /**
+     * 不考虑空格, [消息内容][Message.contentToString]以 [this] 开始则执行 [replier] 并将其返回值回复给发信对象.
+     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
+     */
+    @PlannedRemoval("1.0.0")
+    @Deprecated("use startsWith on your own", replaceWith = ReplaceWith("startsWith(this, true, true, replier)"))
+    open infix fun String.startsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
+        val toCheck = this.trimStart()
+        return content({ it.trim().startsWith(toCheck) }, {
+            executeAndReply(this) { replier(this, it.trim().removePrefix(toCheck)) }
+        })
+    }
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun contains(message: Message, onEvent: MessageListener<M, R>): Ret {
+        return content({ this.message.any { it == message } }, onEvent)
+    }
+
+    @JvmName("case1")
+    @JsName("case1")
+    @PlannedRemoval("1.0.0")
+    @Deprecated("use String.invoke", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this(block)"))
+    infix fun String.`->`(block: MessageListener<M, R>): Ret {
+        return this(block)
+    }
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun containsAll(
+        vararg sub: String,
+        ignoreCase: Boolean = false,
+        trim: Boolean = true,
+        onEvent: MessageListener<M, R>
+    ): Ret = containsAllImpl(sub, ignoreCase = ignoreCase, trim = trim).invoke(onEvent)
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun containsAny(
+        vararg sub: String,
+        ignoreCase: Boolean = false,
+        trim: Boolean = true,
+        onEvent: MessageListener<M, R>
+    ): Ret = containsAnyImpl(*sub, ignoreCase = ignoreCase, trim = trim).invoke(onEvent)
+
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun sentBy(name: String, onEvent: MessageListener<M, R>): Ret =
+        content({ (this as? GroupMessage)?.senderName == name }, onEvent)
+
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun sentByOperator(onEvent: MessageListener<M, R>): Ret =
+        content({ this is GroupMessage && this.sender.isOperator() }, onEvent)
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun sentByAdministrator(onEvent: MessageListener<M, R>): Ret =
+        content({ this is GroupMessage && this.sender.isAdministrator() }, onEvent)
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun sentByOwner(onEvent: MessageListener<M, R>): Ret =
+        content({ this is GroupMessage && this.sender.isOwner() }, onEvent)
+
+    @PlannedRemoval("1.0.0")
+    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
+    fun sentFrom(groupId: Long, onEvent: MessageListener<GroupMessage, R>): Ret =
+        content({ this is GroupMessage && this.group.id == groupId }) { onEvent(this as GroupMessage, it) }
+
+}
+
+/**
+ * DSL 标记. 将能让 IDE 阻止一些错误的方法调用.
+ */
+@Retention(AnnotationRetention.SOURCE)
+@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
+@DslMarker
+annotation class MessageDsl

+ 118 - 0
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt

@@ -0,0 +1,118 @@
+/*
+ * Copyright 2020 Mamoe Technologies and contributors.
+ *
+ * 此源代码的使用受 GNU AFFERO GENERAL PUBLIC LICENSE version 3 许可证的约束, 可以在以下链接找到该许可证.
+ * Use of this source code is governed by the GNU AGPLv3 license that can be found through the following link.
+ *
+ * https://github.com/mamoe/mirai/blob/master/LICENSE
+ */
+
+package net.mamoe.mirai.event.internal
+
+import net.mamoe.mirai.event.MessageDsl
+import net.mamoe.mirai.event.MessageListener
+import net.mamoe.mirai.event.MessageSubscribersBuilder
+import net.mamoe.mirai.message.ContactMessage
+
+
+/*
+ * 将 internal 移出 MessageSubscribersBuilder.kt 以减小其体积
+ */
+
+@MessageDsl
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.content(
+    filter: M.(String) -> Boolean,
+    onEvent: MessageListener<M, RR>
+): Ret =
+    subscriber(filter) { onEvent(this, it) }
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.endsWithImpl(
+    suffix: String,
+    removeSuffix: Boolean = true,
+    trim: Boolean = true,
+    onEvent: @MessageDsl suspend M.(String) -> R
+): Ret {
+    return if (trim) {
+        val toCheck = suffix.trim()
+        content({ it.trimEnd().endsWith(toCheck) }) {
+            if (removeSuffix) this.onEvent(this.message.contentToString().removeSuffix(toCheck).trim())
+            else onEvent(this, this.message.contentToString().trim())
+        }
+    } else {
+        content({ it.endsWith(suffix) }) {
+            if (removeSuffix) this.onEvent(this.message.contentToString().removeSuffix(suffix))
+            else onEvent(this, this.message.contentToString())
+        }
+    }
+}
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.startsWithImpl(
+    prefix: String,
+    removePrefix: Boolean = true,
+    trim: Boolean = true,
+    onEvent: @MessageDsl suspend M.(String) -> R
+): Ret {
+    return if (trim) {
+        val toCheck = prefix.trim()
+        content({ it.trimStart().startsWith(toCheck) }) {
+            if (removePrefix) this.onEvent(this.message.contentToString().substringAfter(toCheck).trim())
+            else onEvent(this, this.message.contentToString().trim())
+        }
+    } else content({ it.startsWith(prefix) }) {
+        if (removePrefix) this.onEvent(this.message.contentToString().removePrefix(prefix))
+        else onEvent(this, this.message.contentToString())
+    }
+}
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAllImpl(
+    sub: Array<out String>,
+    ignoreCase: Boolean = false,
+    trim: Boolean = true
+): MessageSubscribersBuilder<M, Ret, R, RR>.ListeningFilter =
+    if (trim) {
+        val list = sub.map { it.trim() }
+        content { list.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
+    } else {
+        content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
+    }
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsAnyImpl(
+    vararg sub: String,
+    ignoreCase: Boolean = false,
+    trim: Boolean = true
+): MessageSubscribersBuilder<M, Ret, R, RR>.ListeningFilter =
+    if (trim) {
+        val list = sub.map { it.trim() }
+        content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
+    } else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.caseImpl(
+    equals: String,
+    ignoreCase: Boolean = false,
+    trim: Boolean = true
+): MessageSubscribersBuilder<M, Ret, R, RR>.ListeningFilter {
+    return if (trim) {
+        val toCheck = equals.trim()
+        content { it.trim().equals(toCheck, ignoreCase = ignoreCase) }
+    } else {
+        content { it.equals(equals, ignoreCase = ignoreCase) }
+    }
+}
+
+internal fun <M : ContactMessage, Ret, R : RR, RR> MessageSubscribersBuilder<M, Ret, R, RR>.containsImpl(
+    sub: String,
+    ignoreCase: Boolean = false,
+    trim: Boolean = true,
+    onEvent: MessageListener<M, R>
+): Ret {
+    return if (trim) {
+        val toCheck = sub.trim()
+        content({ it.contains(toCheck, ignoreCase = ignoreCase) }) {
+            onEvent(this, this.message.contentToString().trim())
+        }
+    } else {
+        content({ it.contains(sub, ignoreCase = ignoreCase) }) {
+            onEvent(this, this.message.contentToString())
+        }
+    }
+}

+ 1 - 628
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt

@@ -16,28 +16,17 @@ import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.channels.Channel
 import kotlinx.coroutines.channels.ReceiveChannel
 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.event.events.BotEvent
 import net.mamoe.mirai.message.ContactMessage
 import net.mamoe.mirai.message.FriendMessage
 import net.mamoe.mirai.message.GroupMessage
 import net.mamoe.mirai.message.TempMessage
-import net.mamoe.mirai.message.data.At
-import net.mamoe.mirai.message.data.Message
-import net.mamoe.mirai.message.data.firstIsInstance
-import net.mamoe.mirai.message.data.firstIsInstanceOrNull
-import net.mamoe.mirai.utils.PlannedRemoval
 import net.mamoe.mirai.utils.SinceMirai
 import kotlin.contracts.ExperimentalContracts
 import kotlin.contracts.InvocationKind
 import kotlin.contracts.contract
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
-import kotlin.js.JsName
-import kotlin.jvm.JvmName
-import kotlin.jvm.JvmOverloads
 
 typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<ContactMessage, Listener<ContactMessage>, Unit, Unit>
 
@@ -294,620 +283,4 @@ inline fun <reified E : BotEvent> Bot.incoming(
             listener.cancel(CancellationException("ReceiveChannel closed", it))
         }
     }
-}
-
-/**
- * 消息事件的处理器.
- *
- * 注:
- * 接受者 T 为 [ContactMessage]
- * 参数 String 为 转为字符串了的消息 ([Message.toString])
- */
-typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
-
-
-/**
- * 消息订阅构造器
- *
- * @param M 消息类型
- * @param R 消息监听器内部的返回值
- * @param Ret 每个 DSL 函数创建监听器之后的返回值
- *
- * @see subscribeFriendMessages
- */
-@Suppress(
-    "unused", "DSL_SCOPE_VIOLATION_WARNING", "INAPPLICABLE_JVM_NAME", "INVALID_CHARACTERS",
-    "NAME_CONTAINS_ILLEGAL_CHARS", "FunctionName"
-)
-@MessageDsl
-open class MessageSubscribersBuilder<M : ContactMessage, out Ret, R : RR, RR>(
-    val stub: RR,
-    /**
-     * invoke 这个 lambda 时, 它将会把 [消息事件的处理器][MessageListener] 注册给事件, 并返回注册完成返回的监听器.
-     */
-    val subscriber: (M.(String) -> Boolean, MessageListener<M, RR>) -> Ret
-) {
-    @Suppress("DEPRECATION_ERROR")
-    open fun newListeningFilter(filter: M.(String) -> Boolean): ListeningFilter = ListeningFilter(filter)
-
-    /**
-     * 监听的条件
-     */
-    open inner class ListeningFilter @Deprecated( // keep it for development warning
-        "use newListeningFilter instead",
-        ReplaceWith("newListeningFilter(filter)"),
-        level = DeprecationLevel.ERROR
-    ) constructor(
-        val filter: M.(String) -> Boolean
-    ) {
-        /** 进行逻辑 `or`. */
-        infix fun or(another: ListeningFilter): ListeningFilter =
-            newListeningFilter { filter.invoke(this, it) || another.filter.invoke(this, it) }
-
-        /** 进行逻辑 `and`. */
-        infix fun and(another: ListeningFilter): ListeningFilter =
-            newListeningFilter { filter.invoke(this, it) && another.filter.invoke(this, it) }
-
-        /** 进行逻辑 `xor`. */
-        infix fun xor(another: ListeningFilter): ListeningFilter =
-            newListeningFilter { filter.invoke(this, it) xor another.filter.invoke(this, it) }
-
-        /** 进行逻辑 `nand`, 即 `not and`. */
-        infix fun nand(another: ListeningFilter): ListeningFilter =
-            newListeningFilter { !filter.invoke(this, it) || !another.filter.invoke(this, it) }
-
-        /** 进行逻辑 `not` */
-        fun not(): ListeningFilter = newListeningFilter { !filter.invoke(this, it) }
-
-        /** 启动事件监听. */
-        // do not inline due to kotlin (1.3.61) bug: java.lang.IllegalAccessError
-        operator fun invoke(onEvent: MessageListener<M, R>): Ret {
-            return content(filter, onEvent)
-        }
-    }
-
-    /** 启动这个监听器, 在满足条件时回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.reply(toReply: String): Ret {
-        return content(filter) { reply(toReply);[email protected] }
-    }
-
-    /** 启动这个监听器, 在满足条件时回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.reply(message: Message): Ret {
-        return content(filter) { reply(message);[email protected] }
-    }
-
-    /** 启动这个监听器, 在满足条件时回复原消息 */
-    @JvmName("reply3")
-    @SinceMirai("0.33.0")
-    @MessageDsl
-    open infix fun ListeningFilter.`->`(toReply: String): Ret {
-        return this.reply(toReply)
-    }
-
-    /** 启动这个监听器, 在满足条件时回复原消息 */
-    @JvmName("reply3")
-    @SinceMirai("0.33.0")
-    @MessageDsl
-    open infix fun ListeningFilter.`->`(message: Message): Ret {
-        return this.reply(message)
-    }
-
-    /** 启动这个监听器, 在满足条件时回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.reply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
-        return content(filter) {
-            [email protected](this, replier)
-        }
-    }
-
-    /** 启动这个监听器, 在满足条件时引用回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.quoteReply(toReply: String): Ret {
-        return content(filter) { quoteReply(toReply);[email protected] }
-    }
-
-    /** 启动这个监听器, 在满足条件时引用回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.quoteReply(toReply: Message): Ret {
-        return content(filter) { quoteReply(toReply);[email protected] }
-    }
-
-    /** 启动这个监听器, 在满足条件时执行 [replier] 并引用回复原消息 */
-    @SinceMirai("0.29.0")
-    @MessageDsl
-    open infix fun ListeningFilter.quoteReply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
-        return content(filter) {
-            @Suppress("DSL_SCOPE_VIOLATION_WARNING")
-            [email protected](this, replier)
-        }
-    }
-
-
-    /** 无任何触发条件, 每次收到消息都执行 [onEvent] */
-    @MessageDsl
-    open fun always(onEvent: MessageListener<M, RR>): Ret = subscriber({ true }, onEvent)
-
-    /** 如果消息内容 `==` [equals] */
-    @MessageDsl
-    fun case(
-        equals: String,
-        ignoreCase: Boolean = false,
-        trim: Boolean = true
-    ): ListeningFilter {
-        return if (trim) {
-            val toCheck = equals.trim()
-            content { it.trim().equals(toCheck, ignoreCase = ignoreCase) }
-        } else {
-            content { it.equals(equals, ignoreCase = ignoreCase) }
-        }
-    }
-
-    /** 如果消息内容 `==` [equals] */
-    /** 如果消息内容 `==` [equals] */
-    @MessageDsl
-    @SinceMirai("0.37.1")
-    operator fun String.invoke(block: MessageListener<M, R>): Ret {
-        return case(this, onEvent = block)
-    }
-
-    /**
-     * 如果消息内容 `==` [equals]
-     * @param trim `true` 则删除首尾空格后比较
-     * @param ignoreCase `true` 则不区分大小写
-     */
-    @MessageDsl
-    fun case(
-        equals: String,
-        ignoreCase: Boolean = false,
-        trim: Boolean = true,
-        onEvent: MessageListener<M, R>
-    ): Ret {
-        val toCheck = if (trim) equals.trim() else equals
-        return content({ (if (trim) it.trim() else it).equals(toCheck, ignoreCase = ignoreCase) }) {
-            onEvent(this, this.message.contentToString())
-        }
-    }
-
-    /** 如果消息内容包含 [sub] */
-    @MessageDsl
-    fun contains(sub: String): ListeningFilter = content { sub in it }
-
-    /**
-     * 如果消息内容包含 [sub] 中的任意一个元素
-     */
-    @MessageDsl
-    fun contains(
-        sub: String,
-        ignoreCase: Boolean = false,
-        trim: Boolean = true,
-        onEvent: MessageListener<M, R>
-    ): Ret {
-        return if (trim) {
-            val toCheck = sub.trim()
-            content({ it.contains(toCheck, ignoreCase = ignoreCase) }) {
-                onEvent(this, this.message.contentToString().trim())
-            }
-        } else {
-            content({ it.contains(sub, ignoreCase = ignoreCase) }) {
-                onEvent(this, this.message.contentToString())
-            }
-        }
-    }
-
-    /** 如果消息内容包含 [sub] */
-    @JvmOverloads
-    @MessageDsl
-    fun containsAny(vararg sub: String, ignoreCase: Boolean = false, trim: Boolean = true): ListeningFilter =
-        if (trim) {
-            val list = sub.map { it.trim() }
-            content { list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
-        } else content { sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
-
-    /** 如果消息内容包含 [sub] */
-    @JvmOverloads
-    @MessageDsl
-    fun containsAll(vararg sub: String, ignoreCase: Boolean = false, trim: Boolean = true): ListeningFilter =
-        if (trim) {
-            val list = sub.map { it.trim() }
-            content { list.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
-        } else {
-            content { sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } }
-        }
-
-    /** 如果消息的前缀是 [prefix] */
-    @MessageDsl
-    fun startsWith(prefix: String, trim: Boolean = true): ListeningFilter {
-        val toCheck = if (trim) prefix.trim() else prefix
-        return content { (if (trim) it.trim() else it).startsWith(toCheck) }
-    }
-
-    /** 如果消息的前缀是 [prefix] */
-    @MessageDsl
-    fun startsWith(
-        prefix: String,
-        removePrefix: Boolean = true,
-        trim: Boolean = true,
-        onEvent: @MessageDsl suspend M.(String) -> R
-    ): Ret {
-        return if (trim) {
-            val toCheck = prefix.trim()
-            content({ it.trimStart().startsWith(toCheck) }) {
-                if (removePrefix) this.onEvent(this.message.contentToString().substringAfter(toCheck).trim())
-                else onEvent(this, this.message.contentToString().trim())
-            }
-        } else content({ it.startsWith(prefix) }) {
-            if (removePrefix) this.onEvent(this.message.contentToString().removePrefix(prefix))
-            else onEvent(this, this.message.contentToString())
-        }
-    }
-
-    /** 如果消息的结尾是 [suffix] */
-    @MessageDsl
-    fun endsWith(suffix: String): ListeningFilter = content { it.endsWith(suffix) }
-
-    /** 如果消息的结尾是 [suffix] */
-    @MessageDsl
-    fun endsWith(
-        suffix: String,
-        removeSuffix: Boolean = true,
-        trim: Boolean = true,
-        onEvent: @MessageDsl suspend M.(String) -> R
-    ): Ret {
-        return if (trim) {
-            val toCheck = suffix.trim()
-            content({ it.trimEnd().endsWith(toCheck) }) {
-                if (removeSuffix) this.onEvent(this.message.contentToString().removeSuffix(toCheck).trim())
-                else onEvent(this, this.message.contentToString().trim())
-            }
-        } else {
-            content({ it.endsWith(suffix) }) {
-                if (removeSuffix) this.onEvent(this.message.contentToString().removeSuffix(suffix))
-                else onEvent(this, this.message.contentToString())
-            }
-        }
-    }
-
-    /** 如果是这个人发的消息. 消息目前只会是群消息 */
-    @MessageDsl
-    fun sentBy(name: String): ListeningFilter = content { this is GroupMessage && this.senderName == name }
-
-    /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
-    @MessageDsl
-    fun sentBy(qq: Long): ListeningFilter = content { sender.id == qq }
-
-    /** 如果是这个人发的消息. 消息可以是好友消息也可以是群消息 */
-    @MessageDsl
-    fun sentBy(qq: Long, onEvent: MessageListener<M, R>): Ret = content { this.sender.id == qq }.invoke(onEvent)
-
-    /** 如果是好友发来的消息 */
-    @MessageDsl
-    fun sentByFriend(onEvent: MessageListener<FriendMessage, R>): Ret =
-        content({ this is FriendMessage }) {
-            onEvent(this as FriendMessage, it)
-        }
-
-    /** 如果是好友发来的消息 */
-    @MessageDsl
-    fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage }
-
-    /** 如果是好友发来的消息 */
-    @MessageDsl
-    fun sentByTemp(): ListeningFilter = newListeningFilter { this is TempMessage }
-
-    /** 如果是管理员或群主发的消息 */
-    @MessageDsl
-    fun sentByOperator(): ListeningFilter =
-        content { this is GroupMessage && sender.permission.isOperator() }
-
-    /** 如果是管理员发的消息 */
-    @MessageDsl
-    fun sentByAdministrator(): ListeningFilter =
-        content { this is GroupMessage && sender.permission.isAdministrator() }
-
-    /** 如果是群主发的消息 */
-    @MessageDsl
-    fun sentByOwner(): ListeningFilter =
-        content { this is GroupMessage && sender.isOwner() }
-
-    /** 如果是来自这个群的消息 */
-    @MessageDsl
-    fun sentFrom(groupId: Long): ListeningFilter =
-        content { this is GroupMessage && group.id == groupId }
-
-    /** 如果消息内容包含目标为 [Bot] 的 [At] */
-    @MessageDsl
-    fun atBot(): ListeningFilter =
-        content { message.firstIsInstanceOrNull<At>()?.target == bot.id }
-
-    /** 如果消息内容包含目标为 [Bot] 的 [At], 就执行 [onEvent] */
-    @MessageDsl
-    @SinceMirai("0.30.0")
-    fun atBot(onEvent: @MessageDsl suspend M.(String) -> R): Ret =
-        content { message.firstIsInstanceOrNull<At>()?.target == bot.id }.invoke {
-            onEvent.invoke(this, message.contentToString())
-        }
-
-    @MessageDsl
-    @SinceMirai("0.30.0")
-    inline fun <reified N : Message> has(noinline onEvent: @MessageDsl suspend M.(N) -> R): Ret =
-        content { message.any { it is N } }.invoke { onEvent.invoke(this, message.firstIsInstance()) }
-
-    /** 如果消息内容包含 [N] 类型的 [Message] */
-    @MessageDsl
-    inline fun <reified N : Message> has(): ListeningFilter = content { message.any { it is N } }
-
-    /** 如果 [mapper] 返回值非空, 就执行 [onEvent] */
-    @MessageDsl
-    @SinceMirai("0.30.0")
-    open fun <N : Any> mapping(mapper: M.(String) -> N?, onEvent: @MessageDsl suspend M.(N) -> R): Ret =
-        always { onEvent.invoke(this, mapper(this, message.contentToString()) ?: return@always stub) }
-
-    /** 如果 [filter] 返回 `true` */
-    @MessageDsl
-    fun content(filter: M.(String) -> Boolean): ListeningFilter = newListeningFilter(filter)
-
-    /** 如果消息内容可由正则表达式匹配([Regex.matchEntire]) */
-    @MessageDsl
-    fun matching(regex: Regex): ListeningFilter = content { regex.matchEntire(it) != null }
-
-
-    /** 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent` */
-    @MessageDsl
-    fun matching(regex: Regex, onEvent: @MessageDsl suspend M.(MatchResult) -> Unit): Ret =
-        always { executeAndReply(this) { onEvent.invoke(this, regex.matchEntire(it) ?: return@always stub) } }
-
-    /** 如果消息内容可由正则表达式查找([Regex.find]) */
-    @MessageDsl
-    fun finding(regex: Regex): ListeningFilter = content { regex.find(it) != null }
-
-    /** 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent` */
-    @MessageDsl
-    fun finding(regex: Regex, onEvent: @MessageDsl suspend M.(MatchResult) -> Unit): Ret =
-        always { executeAndReply(this) { onEvent.invoke(this, regex.find(it) ?: return@always stub) } }
-
-
-    /** 若消息内容包含 [this] 则回复 [reply] */
-    @MessageDsl
-    open infix fun String.containsReply(reply: String): Ret =
-        content({ this@containsReply in it }, { reply(reply); stub })
-
-    /**
-     * 若消息内容包含 [this] 则执行 [replier] 并将其返回值回复给发信对象.
-     *
-     * [replier] 的 `it` 将会是消息内容 string.
-     *
-     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
-     */
-    @MessageDsl
-    open infix fun String.containsReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret =
-        content({ this@containsReply in it }, { executeAndReply(this, replier) })
-
-    /**
-     * 若消息内容可由正则表达式匹配([Regex.matchEntire]), 则执行 [replier] 并将其返回值回复给发信对象.
-     *
-     * [replier] 的 `it` 将会是消息内容 string.
-     *
-     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
-     */
-    @MessageDsl
-    open infix fun Regex.matchingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
-        always { executeAndReply(this) { replier.invoke(this, matchEntire(it) ?: return@always stub) } }
-
-    /**
-     * 若消息内容可由正则表达式查找([Regex.find]), 则执行 [replier] 并将其返回值回复给发信对象.
-     *
-     * [replier] 的 `it` 将会是消息内容 string.
-     *
-     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
-     */
-    @MessageDsl
-    open infix fun Regex.findingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
-        always { executeAndReply(this) { replier.invoke(this, [email protected](it) ?: return@always stub) } }
-
-    /**
-     * 不考虑空格, 若消息内容以 [this] 开始则执行 [replier] 并将其返回值回复给发信对象.
-     *
-     * [replier] 的 `it` 将会是去掉用来判断的前缀并删除前后空格后的字符串.
-     * 如当消息为 "kick    123456     " 时
-     * ```kotlin
-     * "kick" startsWithReply {
-     *     println(it) // it 为 "123456"
-     * }
-     * ```
-     *
-     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
-     */
-    @PlannedRemoval("1.0.0")
-    @Deprecated("use startsWith on your own", replaceWith = ReplaceWith("startsWith(this, true, true, replier)"))
-    @MessageDsl
-    open infix fun String.startsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
-        val toCheck = this.trimStart()
-        return content({ it.trim().startsWith(toCheck) }, {
-            executeAndReply(this) { replier(this, it.trim().removePrefix(toCheck)) }
-        })
-    }
-
-    /**
-     * 不考虑空格, 若消息内容以 [this] 结尾则执行 [replier] 并将其返回值回复给发信对象.
-     *
-     * [replier] 的 `it` 将会是去掉用来判断的后缀并删除前后空格后的字符串.
-     * 如当消息为 "  123456 test" 时
-     * ```kotlin
-     * "test" endsWithReply {
-     *     println(it) // it 为 "123456"
-     * }
-     * ```
-     *
-     * @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
-     */
-    @MessageDsl
-    open infix fun String.endsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
-        val toCheck = this.trimEnd()
-        return content({ it.trim().endsWith(toCheck) }, {
-            executeAndReply(this) { replier(this, it.trim().removeSuffix(toCheck)) }
-        })
-    }
-
-    /** 当发送的消息内容为 [this] 就回复 [reply] */
-    @MessageDsl
-    open infix fun String.reply(reply: String): Ret {
-        val toCheck = this.trim()
-        return content({ it.trim() == toCheck }, { reply(reply);[email protected] })
-    }
-
-    /** 当发送的消息内容为 [this] 就回复 [reply] */
-    @MessageDsl
-    open infix fun String.reply(reply: Message): Ret {
-        val toCheck = this.trim()
-        return content({ it.trim() == toCheck }, { reply(reply);[email protected] })
-    }
-
-    /** 当发送的消息内容为 [this] 就执行并回复 [replier] 的返回值 */
-    @MessageDsl
-    open infix fun String.reply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
-        val toCheck = this.trim()
-        return content({ it.trim() == toCheck }, {
-            @Suppress("DSL_SCOPE_VIOLATION_WARNING")
-            executeAndReply(this) { replier(this, it.trim()) }
-        })
-    }
-
-    @PublishedApi
-    @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE", "UNCHECKED_CAST") // false positive
-    internal suspend inline fun executeAndReply(m: M, replier: suspend M.(String) -> Any?): RR {
-        when (val message = replier(m, m.message.contentToString())) {
-            is Message -> m.reply(message)
-            is Unit -> Unit
-            else -> m.reply(message.toString())
-        }
-        return stub
-    }
-
-    @PublishedApi
-    @Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE", "UNCHECKED_CAST") // false positive
-    internal suspend inline fun executeAndQuoteReply(m: M, replier: suspend M.(String) -> Any?): RR {
-        when (val message = replier(m, m.message.contentToString())) {
-            is Message -> m.quoteReply(message)
-            is Unit -> Unit
-            else -> m.quoteReply(message.toString())
-        }
-        return stub
-    }
-
-    ////////////////////
-    //// DEPRECATED ////
-    ////////////////////
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun contains(message: Message, onEvent: MessageListener<M, R>): Ret {
-        return content({ this.message.any { it == message } }, onEvent)
-    }
-
-    @MessageDsl
-    @JvmName("case1")
-    @JsName("case1")
-    @PlannedRemoval("1.0.0")
-    @SinceMirai("0.29.0")
-    @Deprecated("use String.invoke", level = DeprecationLevel.ERROR, replaceWith = ReplaceWith("this(block)"))
-    infix fun String.`->`(block: MessageListener<M, R>): Ret {
-        return this(block)
-    }
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun containsAll(
-        vararg sub: String,
-        ignoreCase: Boolean = false,
-        trim: Boolean = true,
-        onEvent: MessageListener<M, R>
-    ): Ret {
-        return if (trim) {
-            val list = sub.map { it.trim() }
-            content({ list.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } },
-                { onEvent(this, this.message.contentToString().trim()) })
-        } else {
-            content({ sub.all { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) } },
-                { onEvent(this, this.message.contentToString()) })
-        }
-    }
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun containsAny(
-        vararg sub: String,
-        ignoreCase: Boolean = false,
-        trim: Boolean = true,
-        onEvent: MessageListener<M, R>
-    ): Ret {
-        return if (trim) {
-            val list = sub.map { it.trim() }
-            content({
-                list.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) }
-            }, {
-                onEvent(this, this.message.contentToString().trim())
-            })
-        } else {
-            content({
-                sub.any { toCheck -> it.contains(toCheck, ignoreCase = ignoreCase) }
-            }, {
-                onEvent(this, this.message.contentToString())
-            })
-        }
-    }
-
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun sentBy(name: String, onEvent: MessageListener<M, R>): Ret =
-        content({ (this as? GroupMessage)?.senderName == name }, onEvent)
-
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun sentByOperator(onEvent: MessageListener<M, R>): Ret =
-        content({ this is GroupMessage && this.sender.isOperator() }, onEvent)
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun sentByAdministrator(onEvent: MessageListener<M, R>): Ret =
-        content({ this is GroupMessage && this.sender.isAdministrator() }, onEvent)
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun sentByOwner(onEvent: MessageListener<M, R>): Ret =
-        content({ this is GroupMessage && this.sender.isOwner() }, onEvent)
-
-    @PlannedRemoval("1.0.0")
-    @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN)
-    @MessageDsl
-    fun sentFrom(groupId: Long, onEvent: MessageListener<GroupMessage, R>): Ret =
-        content({ this is GroupMessage && this.group.id == groupId }) { onEvent(this as GroupMessage, it) }
-
-
-    @MessageDsl
-    internal fun content(filter: M.(String) -> Boolean, onEvent: MessageListener<M, RR>): Ret =
-        subscriber(filter) { onEvent(this, it) }
-}
-
-/**
- * DSL 标记. 将能让 IDE 阻止一些错误的方法调用.
- */
-@Retention(AnnotationRetention.SOURCE)
-@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE)
-@DslMarker
-annotation class MessageDsl
+}