mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-09 01:30:17 +08:00
Rearrange MessageSubscribersBuilder
This commit is contained in:
parent
e31805a835
commit
a5b82c5e48
@ -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 -> 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<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, 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<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
|
@ -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())
|
||||
}
|
||||
}
|
||||
}
|
@ -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>
|
||||
|
||||
@ -295,619 +284,3 @@ inline fun <reified E : BotEvent> Bot.incoming(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息事件的处理器.
|
||||
*
|
||||
* 注:
|
||||
* 接受者 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);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<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, 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<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
|
Loading…
Reference in New Issue
Block a user