From a5b82c5e481c30d239e1806bf05f7917e33f17dd Mon Sep 17 00:00:00 2001 From: Him188 Date: Wed, 15 Apr 2020 10:11:09 +0800 Subject: [PATCH] Rearrange `MessageSubscribersBuilder` --- .../event/MessageSubscribersBuilder.kt | 508 ++++++++++++++ .../internal/messageSubscribersInternal.kt | 118 ++++ .../event/subscribeMessages.kt | 629 +----------------- 3 files changed, 627 insertions(+), 628 deletions(-) create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt create mode 100644 mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/MessageSubscribersBuilder.kt new file mode 100644 index 000000000..c2cb244cd --- /dev/null +++ b/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 = @MessageDsl suspend T.(String) -> R + + +/** + * 消息订阅构造器 + * + * @param M 消息类型 + * @param R 消息监听器内部的返回值 + * @param Ret 每个 DSL 函数创建监听器之后的返回值 + * + * @see subscribeFriendMessages + */ +@MessageDsl +open class MessageSubscribersBuilder( + /** + * 用于 [MessageListener] 无返回值的替代. + */ + @PublishedApi + internal val stub: RR, + /** + * invoke 这个 lambda 时, 它将会把 [消息事件的处理器][MessageListener] 注册给事件, 并返回注册完成返回的监听器. + */ + val subscriber: (M.(String) -> Boolean, MessageListener) -> 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): 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): 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): Ret = case(this, onEvent = block) + + /** 如果[消息内容][Message.contentToString] [matches] */ + @MessageDsl + @JvmSynthetic + @JvmName("matchingExtension") + infix fun Regex.matching(block: MessageListener): 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 -> this@finding.find(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 + ): 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 + ): 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): Ret = content { this.sender.id == qq }.invoke(onEvent) + + /** 如果是好友发来的消息 */ + @MessageDsl + fun sentByFriend(onEvent: MessageListener): 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()?.target == bot.id } + + /** [消息内容][Message.contentToString]包含目标为 [Bot] 的 [At], 就执行 [onEvent] */ + @MessageDsl + fun atBot(onEvent: @MessageDsl suspend M.(String) -> R): Ret = + content { message.firstIsInstanceOrNull()?.target == bot.id }.invoke { + onEvent.invoke(this, message.contentToString()) + } + + @MessageDsl + inline fun 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 has(): ListeningFilter = content { message.any { it is N } } + + /** 如果 [mapper] 返回值非空, 就执行 [onEvent] */ + @MessageDsl + open fun 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, this@findingReply.find(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): 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): 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 + ): 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 + ): 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): Ret = + content({ (this as? GroupMessage)?.senderName == name }, onEvent) + + + @PlannedRemoval("1.0.0") + @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) + fun sentByOperator(onEvent: MessageListener): Ret = + content({ this is GroupMessage && this.sender.isOperator() }, onEvent) + + @PlannedRemoval("1.0.0") + @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) + fun sentByAdministrator(onEvent: MessageListener): Ret = + content({ this is GroupMessage && this.sender.isAdministrator() }, onEvent) + + @PlannedRemoval("1.0.0") + @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) + fun sentByOwner(onEvent: MessageListener): 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): 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 \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/internal/messageSubscribersInternal.kt new file mode 100644 index 000000000..36393ae31 --- /dev/null +++ b/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 MessageSubscribersBuilder.content( + filter: M.(String) -> Boolean, + onEvent: MessageListener +): Ret = + subscriber(filter) { onEvent(this, it) } + +internal fun MessageSubscribersBuilder.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 MessageSubscribersBuilder.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 MessageSubscribersBuilder.containsAllImpl( + sub: Array, + ignoreCase: Boolean = false, + trim: Boolean = true +): MessageSubscribersBuilder.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 MessageSubscribersBuilder.containsAnyImpl( + vararg sub: String, + ignoreCase: Boolean = false, + trim: Boolean = true +): MessageSubscribersBuilder.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 MessageSubscribersBuilder.caseImpl( + equals: String, + ignoreCase: Boolean = false, + trim: Boolean = true +): MessageSubscribersBuilder.ListeningFilter { + return if (trim) { + val toCheck = equals.trim() + content { it.trim().equals(toCheck, ignoreCase = ignoreCase) } + } else { + content { it.equals(equals, ignoreCase = ignoreCase) } + } +} + +internal fun MessageSubscribersBuilder.containsImpl( + sub: String, + ignoreCase: Boolean = false, + trim: Boolean = true, + onEvent: MessageListener +): 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()) + } + } +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt index a2fb38ff4..b321dfc59 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event/subscribeMessages.kt +++ b/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, Unit, Unit> @@ -294,620 +283,4 @@ inline fun Bot.incoming( listener.cancel(CancellationException("ReceiveChannel closed", it)) } } -} - -/** - * 消息事件的处理器. - * - * 注: - * 接受者 T 为 [ContactMessage] - * 参数 String 为 转为字符串了的消息 ([Message.toString]) - */ -typealias MessageListener = @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( - val stub: RR, - /** - * invoke 这个 lambda 时, 它将会把 [消息事件的处理器][MessageListener] 注册给事件, 并返回注册完成返回的监听器. - */ - val subscriber: (M.(String) -> Boolean, MessageListener) -> 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): Ret { - return content(filter, onEvent) - } - } - - /** 启动这个监听器, 在满足条件时回复原消息 */ - @SinceMirai("0.29.0") - @MessageDsl - open infix fun ListeningFilter.reply(toReply: String): Ret { - return content(filter) { reply(toReply);this@MessageSubscribersBuilder.stub } - } - - /** 启动这个监听器, 在满足条件时回复原消息 */ - @SinceMirai("0.29.0") - @MessageDsl - open infix fun ListeningFilter.reply(message: Message): Ret { - return content(filter) { reply(message);this@MessageSubscribersBuilder.stub } - } - - /** 启动这个监听器, 在满足条件时回复原消息 */ - @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) { - this@MessageSubscribersBuilder.executeAndReply(this, replier) - } - } - - /** 启动这个监听器, 在满足条件时引用回复原消息 */ - @SinceMirai("0.29.0") - @MessageDsl - open infix fun ListeningFilter.quoteReply(toReply: String): Ret { - return content(filter) { quoteReply(toReply);this@MessageSubscribersBuilder.stub } - } - - /** 启动这个监听器, 在满足条件时引用回复原消息 */ - @SinceMirai("0.29.0") - @MessageDsl - open infix fun ListeningFilter.quoteReply(toReply: Message): Ret { - return content(filter) { quoteReply(toReply);this@MessageSubscribersBuilder.stub } - } - - /** 启动这个监听器, 在满足条件时执行 [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") - this@MessageSubscribersBuilder.executeAndQuoteReply(this, replier) - } - } - - - /** 无任何触发条件, 每次收到消息都执行 [onEvent] */ - @MessageDsl - open fun always(onEvent: MessageListener): 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): 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 - ): 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 - ): 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): Ret = content { this.sender.id == qq }.invoke(onEvent) - - /** 如果是好友发来的消息 */ - @MessageDsl - fun sentByFriend(onEvent: MessageListener): 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()?.target == bot.id } - - /** 如果消息内容包含目标为 [Bot] 的 [At], 就执行 [onEvent] */ - @MessageDsl - @SinceMirai("0.30.0") - fun atBot(onEvent: @MessageDsl suspend M.(String) -> R): Ret = - content { message.firstIsInstanceOrNull()?.target == bot.id }.invoke { - onEvent.invoke(this, message.contentToString()) - } - - @MessageDsl - @SinceMirai("0.30.0") - inline fun 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 has(): ListeningFilter = content { message.any { it is N } } - - /** 如果 [mapper] 返回值非空, 就执行 [onEvent] */ - @MessageDsl - @SinceMirai("0.30.0") - open fun 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, this@findingReply.find(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);this@MessageSubscribersBuilder.stub }) - } - - /** 当发送的消息内容为 [this] 就回复 [reply] */ - @MessageDsl - open infix fun String.reply(reply: Message): Ret { - val toCheck = this.trim() - return content({ it.trim() == toCheck }, { reply(reply);this@MessageSubscribersBuilder.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()) } - }) - } - - @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): 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): 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 - ): 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 - ): 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): Ret = - content({ (this as? GroupMessage)?.senderName == name }, onEvent) - - - @PlannedRemoval("1.0.0") - @Deprecated("for binary compatibility", level = DeprecationLevel.HIDDEN) - @MessageDsl - fun sentByOperator(onEvent: MessageListener): 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): 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): 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): Ret = - content({ this is GroupMessage && this.group.id == groupId }) { onEvent(this as GroupMessage, it) } - - - @MessageDsl - internal fun content(filter: M.(String) -> Boolean, onEvent: MessageListener): Ret = - subscriber(filter) { onEvent(this, it) } -} - -/** - * DSL 标记. 将能让 IDE 阻止一些错误的方法调用. - */ -@Retention(AnnotationRetention.SOURCE) -@Target(AnnotationTarget.FUNCTION, AnnotationTarget.CLASS, AnnotationTarget.TYPE) -@DslMarker -annotation class MessageDsl \ No newline at end of file +} \ No newline at end of file