1
0
mirror of https://github.com/mamoe/mirai.git synced 2025-04-25 04:50:26 +08:00

multi and complex conditions supported for MessageSubscribers

This commit is contained in:
Him188 2020-01-19 18:18:00 +08:00
parent 3ddbdbf649
commit 3390580658
2 changed files with 203 additions and 77 deletions
mirai-core/src/commonMain/kotlin/net.mamoe.mirai/event
mirai-demos/mirai-demo-gentleman/src/main/kotlin/demo/gentleman

View File

@ -111,6 +111,9 @@ inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscribers
}.apply { listeners() }
}
typealias MessageListener<T> = @MessageDsl suspend T.(String) -> Unit
/**
* 消息订阅构造器
*
@ -121,130 +124,245 @@ inline fun Bot.subscribeFriendMessages(crossinline listeners: MessageSubscribers
@Suppress("unused")
@MessageDsl
class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
val subscriber: (@MessageDsl suspend T.(String) -> Unit) -> Listener<T>
val subscriber: (MessageListener<T>) -> Listener<T>
) {
/**
* 监听的条件
*/
inner class ListeningFilter(
val filter: T.(String) -> Boolean
) {
/**
* 进行逻辑 `or`.
*/
infix fun or(another: ListeningFilter): ListeningFilter =
ListeningFilter { filter.invoke(this, it) || another.filter.invoke(this, it) }
/**
* 进行逻辑 `and`.
*/
infix fun and(another: ListeningFilter): ListeningFilter =
ListeningFilter { filter.invoke(this, it) && another.filter.invoke(this, it) }
/**
* 进行逻辑 `xor`.
*/
infix fun xor(another: ListeningFilter): ListeningFilter =
ListeningFilter { filter.invoke(this, it) xor another.filter.invoke(this, it) }
/**
* 进行逻辑 `nand`, `not and`.
*/
infix fun nand(another: ListeningFilter): ListeningFilter =
ListeningFilter { !filter.invoke(this, it) || !another.filter.invoke(this, it) }
/**
* 启动时间监听.
*/
// do not inline due to kotlin (1.3.61) bug: java.lang.IllegalAccessError
operator fun invoke(onEvent: MessageListener<T>): Listener<T> {
return content(filter, onEvent)
}
}
/**
* 无任何触发条件.
*/
@MessageDsl
fun always(onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> {
return subscriber(onEvent)
}
fun always(onEvent: MessageListener<T>): Listener<T> = subscriber(onEvent)
/**
* 如果消息内容 `==` [equals], 就执行 [onEvent]
* 如果消息内容 `==` [equals]
* @param trim `true` 则删除首尾空格后比较
* @param ignoreCase `true` 则不区分大小写
*/
@MessageDsl
inline fun case(
equals: String,
trim: Boolean = true,
ignoreCase: Boolean = false,
trim: Boolean = true,
crossinline onEvent: @MessageDsl suspend T.(String) -> Unit
): Listener<T> {
val toCheck = if (trim) equals.trim() else equals
return content({ toCheck.equals(if (trim) it.trim() else it, ignoreCase = ignoreCase) }, onEvent)
return content({ (if (trim) it.trim() else it).equals(toCheck, ignoreCase = ignoreCase) }, {
onEvent(this, this.message.toString())
})
}
/**
* 如果消息内容包含 [sub], 就执行 [onEvent]
* 如果消息内容包含 [sub]
*/
@MessageDsl
inline fun contains(sub: String, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> = content({ sub in it }, onEvent)
fun contains(sub: String): ListeningFilter =
content { sub in it }
/**
* 如果消息的前缀是 [prefix], 就执行 [onEvent]
* 如果消息内容包含 [sub]
*/
inline fun contains(
sub: String,
ignoreCase: Boolean = false,
trim: Boolean = true,
crossinline onEvent: MessageListener<T>
): Listener<T> {
val toCheck = if (trim) sub.trim() else sub
return content({ (if (trim) it.trim() else it).contains(toCheck, ignoreCase = ignoreCase) }, {
onEvent(this, this.message.toString())
})
}
/**
* 如果消息的前缀是 [prefix]
*/
@MessageDsl
inline fun startsWith(
prefix: String,
removePrefix: Boolean = true,
trim: Boolean = true,
crossinline onEvent: @MessageDsl suspend T.(String) -> Unit
): Listener<T> =
content({ it.startsWith(prefix) }) {
if (removePrefix) this.onEvent(this.message.toString().substringAfter(prefix))
): Listener<T> {
val toCheck = if (trim) prefix.trim() else prefix
return content({ (if (trim) it.trim() else it).startsWith(toCheck) }, {
if (removePrefix) this.onEvent(this.message.toString().substringAfter(toCheck))
else onEvent(this, this.message.toString())
}
})
}
/**
* 如果消息的结尾是 [suffix], 就执行 [onEvent]
* 如果消息的结尾是 [suffix]
*/
@MessageDsl
inline fun endsWith(suffix: String, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ it.endsWith(suffix) }, onEvent)
fun endsWith(suffix: String): ListeningFilter =
content { it.endsWith(suffix) }
/**
* 如果是这个人发的消息, 就执行 [onEvent]. 消息可以是好友消息也可以是群消息
* 如果是这个人发的消息. 消息可以是好友消息也可以是群消息
*/
@MessageDsl
inline fun sentBy(qqId: Long, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ sender.id == qqId }, onEvent)
fun sentBy(qqId: Long): ListeningFilter =
content { sender.id == qqId }
/**
* 如果是管理员或群主发的消息, 就执行 [onEvent]
* 如果是这个人发的消息. 消息可以是好友消息也可以是群消息
*/
@MessageDsl
inline fun sentByOperator(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ this is GroupMessage && sender.permission.isOperator() }, onEvent)
inline fun sentBy(qqId: Long, crossinline onEvent: MessageListener<T>): Listener<T> =
content({ this.sender.id == qqId }, onEvent)
/**
* 如果是管理员发的消息, 就执行 [onEvent]
* 如果是管理员或群主发的消息
*/
@MessageDsl
inline fun sentByAdministrator(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ this is GroupMessage && sender.permission.isAdministrator() }, onEvent)
fun sentByOperator(): ListeningFilter =
content { this is GroupMessage && sender.permission.isOperator() }
/**
* 如果是群主发的消息, 就执行 [onEvent]
* 如果是管理员或群主发的消息
*/
@MessageDsl
inline fun sentByOwner(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ this is GroupMessage && sender.permission.isOwner() }, onEvent)
inline fun sentByOperator(crossinline onEvent: MessageListener<T>): Listener<T> =
content({ this is GroupMessage && this.sender.isOperator() }, onEvent)
/**
* 如果是管理员发的消息
*/
@MessageDsl
fun sentByAdministrator(): ListeningFilter =
content { this is GroupMessage && sender.permission.isAdministrator() }
/**
* 如果是管理员发的消息
*/
@MessageDsl
inline fun sentByAdministrator(crossinline onEvent: MessageListener<T>): Listener<T> =
content({ this is GroupMessage && this.sender.isAdministrator() }, onEvent)
/**
* 如果是群主发的消息
*/
@MessageDsl
fun sentByOwner(): ListeningFilter =
content { this is GroupMessage && sender.isOwner() }
/**
* 如果是群主发的消息
*/
@MessageDsl
inline fun sentByOwner(crossinline onEvent: MessageListener<T>): Listener<T> =
content({ this is GroupMessage && this.sender.isOwner() }, onEvent)
/**
* 如果是来自这个群的消息
*/
@MessageDsl
fun sentFrom(groupId: Long): ListeningFilter =
content { this is GroupMessage && group.id == groupId }
/**
* 如果是来自这个群的消息, 就执行 [onEvent]
*/
@MessageDsl
inline fun sentFrom(id: Long, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
content({ if (this is GroupMessage) group.id == id else false }, onEvent)
inline fun sentFrom(groupId: Long, crossinline onEvent: MessageListener<T>): Listener<T> =
content({ this is GroupMessage && this.group.id == groupId }, onEvent)
/**
* 如果消息内容包含 [M] 类型的 [Message]
*/
@MessageDsl
inline fun <reified M : Message> has(): ListeningFilter =
content { message.any { it::class == M::class } }
/**
* 如果消息内容包含 [M] 类型的 [Message], 就执行 [onEvent]
*/
@MessageDsl
inline fun <reified M : Message> has(crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
subscriber { if (message.any { it::class == M::class }) onEvent(this, this.message.toString()) }
inline fun <reified M : Message> has(crossinline onEvent: MessageListener<T>): Listener<T> =
content({ message.any { it::class == M::class } }, onEvent)
/**
* 如果 [filter] 返回 `true`
*/
@MessageDsl
inline fun content(crossinline filter: T.(String) -> Boolean): ListeningFilter =
ListeningFilter { filter(this, it) }
/**
* 如果 [filter] 返回 `true` 就执行 `onEvent`
*/
@MessageDsl
inline fun content(crossinline filter: T.(String) -> Boolean, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit): Listener<T> =
subscriber { if (this.filter(message.toString())) onEvent(this, this.message.toString()) }
inline fun content(crossinline filter: T.(String) -> Boolean, crossinline onEvent: MessageListener<T>): Listener<T> =
subscriber {
if (filter(this, it)) onEvent(this, it)
}
/**
* 如果消息内容可由正则表达式匹配([Regex.matchEntire]), 就执行 `onEvent`
*/
@MessageDsl
inline fun matching(regex: Regex, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
fun matching(regex: Regex): ListeningFilter =
content { regex.matchEntire(it) != null }
/**
* 如果 [filter] 返回 `true` 就执行 `onEvent`
*/
@MessageDsl
inline fun matching(regex: Regex, crossinline onEvent: MessageListener<T>): Listener<T> =
content({ regex.matchEntire(it) != null }, onEvent)
}
/**
* 如果消息内容可由正则表达式查找([Regex.find]), 就执行 `onEvent`
*/
@MessageDsl
inline fun finding(regex: Regex, crossinline onEvent: @MessageDsl suspend T.(String) -> Unit) {
content({ regex.find(it) != null }, onEvent)
}
fun finding(regex: Regex): ListeningFilter =
content { regex.find(it) != null }
/**
* 若消息内容包含 [this] 则回复 [reply]
*/
@MessageDsl
infix fun String.containsReply(reply: String) =
content({ this@containsReply in it }) { this@content.reply(reply) }
infix fun String.containsReply(reply: String): Listener<T> =
content({ this@containsReply in it }, { reply(reply) })
/**
* 若消息内容包含 [this] 则执行 [replier] 并将其返回值回复给发信对象.
@ -254,11 +372,11 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
inline infix fun String.containsReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) =
content({ this@containsReply in it }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier)
}
inline infix fun String.containsReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> =
content({ this@containsReply in it }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply(replier)
})
/**
* 若消息内容可由正则表达式匹配([Regex.matchEntire]), 则执行 [replier] 并将其返回值回复给发信对象.
@ -268,12 +386,11 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
inline infix fun Regex.matchingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
content({ this@matchingReply.matchEntire(it) != null }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier)
}
}
inline infix fun Regex.matchingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> =
content({ this@matchingReply.matchEntire(it) != null }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply(replier)
})
/**
* 若消息内容可由正则表达式查找([Regex.find]), 则执行 [replier] 并将其返回值回复给发信对象.
@ -283,12 +400,11 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
inline infix fun Regex.findingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
content({ this@findingReply.find(it) != null }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier)
}
}
inline infix fun Regex.findingReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> =
content({ this@findingReply.find(it) != null }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply(replier)
})
/**
* 不考虑空格, 若消息内容以 [this] 开始则执行 [replier] 并将其返回值回复给发信对象.
@ -304,14 +420,14 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
*/
@MessageDsl
inline infix fun String.startsWithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
inline infix fun String.startsWithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> {
val toCheck = this.trimStart()
content({ it.trimStart().startsWith(toCheck) }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply {
replier(it.removePrefix(toCheck).trim())
return content({ it.trim().startsWith(toCheck) }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply {
replier(this, it.trim().removePrefix(toCheck))
}
}
})
}
/**
@ -328,30 +444,36 @@ class MessageSubscribersBuilder<T : MessagePacket<*, *>>(
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
inline infix fun String.endswithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) {
inline infix fun String.endswithReply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> {
val toCheck = this.trimEnd()
content({ it.endsWith(this@endswithReply) }) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply {
replier(it.removeSuffix(toCheck).trim())
return content({ it.trim().endsWith(toCheck) }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply {
replier(this, it.trim().removeSuffix(toCheck))
}
}
})
}
@MessageDsl
infix fun String.reply(reply: String) = case(this) {
this@case.reply(reply)
infix fun String.reply(reply: String): Listener<T> {
val toCheck = this.trim()
return content({ it.trim() == toCheck }, { reply(reply) })
}
@MessageDsl
inline infix fun String.reply(crossinline replier: @MessageDsl suspend T.(String) -> Any?) = case(this) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING") // false negative warning
executeAndReply(replier)
inline infix fun String.reply(crossinline replier: @MessageDsl suspend T.(String) -> Any?): Listener<T> {
val toCheck = this.trim()
return content({ it.trim() == toCheck }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply {
replier(this, it.trim())
}
})
}
@PublishedApi
@Suppress("REDUNDANT_INLINE_SUSPEND_FUNCTION_TYPE") // false positive
internal suspend inline fun T.executeAndReply(replier: @MessageDsl suspend T.(String) -> Any?) {
internal suspend inline fun T.executeAndReply(replier: suspend T.(String) -> Any?) {
when (val message = replier(this, this.message.toString())) {
is Message -> this.reply(message)
is Unit -> {

View File

@ -138,6 +138,10 @@ suspend fun main() {
}.reply()
}
(contains("1") and has<Image>()){
reply("Your message has a string \"1\" and an image contained")
}
has<Image> {
if (this is FriendMessage || (this is GroupMessage && this.permission == MemberPermission.ADMINISTRATOR)) withContext(IO) {
val image: Image by message