Prohibit unsafe usage

This commit is contained in:
Him188 2020-03-22 16:24:58 +08:00
parent 8e37cdbf93
commit 6bb8231d8e
2 changed files with 134 additions and 64 deletions

View File

@ -11,6 +11,7 @@ package net.mamoe.mirai.event
import kotlinx.coroutines.*
import net.mamoe.mirai.message.MessagePacket
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.message.isContextIdenticalWith
import net.mamoe.mirai.message.nextMessage
import net.mamoe.mirai.utils.MiraiExperimentalAPI
@ -50,7 +51,6 @@ import kotlin.jvm.JvmSynthetic
*/
@SinceMirai("0.29.0")
@Suppress("unused")
@MiraiExperimentalAPI
suspend inline fun <reified T : MessagePacket<*, *>> T.whileSelectMessages(
timeoutMillis: Long = -1,
crossinline selectBuilder: @MessageDsl MessageSelectBuilder<T, Boolean>.() -> Unit
@ -122,7 +122,6 @@ suspend inline fun <reified T : MessagePacket<*, *>> T.selectMessagesUnit(
*
* @see nextMessage 挂起协程并等待下一条消息
*/
@MiraiExperimentalAPI
@SinceMirai("0.29.0")
@Suppress("unused") // false positive
@OptIn(ExperimentalTypeInference::class)
@ -165,10 +164,69 @@ suspend inline fun <reified T : MessagePacket<*, *>, R> T.selectMessages(
}
@SinceMirai("0.29.0")
class MessageSelectBuilder<M : MessagePacket<*, *>, R> @PublishedApi internal constructor(
open class MessageSelectBuilder<M : MessagePacket<*, *>, R> @PublishedApi internal constructor(
stub: Any?,
subscriber: (M.(String) -> Boolean, MessageListener<M, Any?>) -> Unit
) : MessageSubscribersBuilder<M, Unit, R, Any?>(stub, subscriber)
) : MessageSubscribersBuilder<M, Unit, R, Any?>(stub, subscriber) {
/**
* 无任何触发条件.
*/
@MessageDsl
fun default(onEvent: MessageListener<M, R>): Unit = subscriber({ true }, onEvent)
@Deprecated("Use `default` instead", level = DeprecationLevel.HIDDEN)
override fun always(onEvent: MessageListener<M, Any?>) {
super.always(onEvent)
}
// 这些函数无法获取返回值. 必须屏蔽.
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.containsReply(reply: String): Nothing = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.containsReply(replier: suspend M.(String) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun Regex.matchingReply(replier: suspend M.(MatchResult) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun Regex.findingReply(replier: suspend M.(MatchResult) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.startsWithReply(replier: suspend M.(String) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.endsWithReply(replier: suspend M.(String) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.reply(reply: String) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.reply(reply: Message) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun String.reply(replier: suspend M.(String) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.reply(toReply: String) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.reply(message: Message) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.reply(replier: suspend M.(String) -> Any?) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.quoteReply(toReply: String) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.quoteReply(message: Message) = error("prohibited")
@Deprecated("Using `reply` DSL in message selection is prohibited", level = DeprecationLevel.HIDDEN)
override fun ListeningFilter.quoteReply(replier: suspend M.(String) -> Any?) = error("prohibited")
}
@JvmSynthetic
@PublishedApi

View File

@ -24,7 +24,6 @@ import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.MessagePacket
import net.mamoe.mirai.message.data.Message
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.SinceMirai
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
@ -34,6 +33,8 @@ import kotlin.coroutines.EmptyCoroutineContext
import kotlin.js.JsName
import kotlin.jvm.JvmName
typealias MessagePacketSubscribersBuilder = MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有联系人的消息事件. 联系人可以是任意群或任意好友或临时会话.
*
@ -43,14 +44,14 @@ import kotlin.jvm.JvmName
fun <R> CoroutineScope.subscribeMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>.() -> R
listeners: MessagePacketSubscribersBuilder.() -> R
): R {
// contract 可帮助 IDE 进行类型推断. 无实际代码作用.
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>(Unit)
return MessagePacketSubscribersBuilder(Unit)
{ filter, messageListener: MessageListener<MessagePacket<*, *>, Unit> ->
// subscribeAlways 即注册一个监听器. 这个监听器收到消息后就传递给 [messageListener]
// messageListener 即为 DSL 里 `contains(...) { }`, `startsWith(...) { }` 的代码块.
@ -63,6 +64,8 @@ fun <R> CoroutineScope.subscribeMessages(
}.run(listeners)
}
typealias GroupMessageSubscribersBuilder = MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有群消息事件
*
@ -72,12 +75,12 @@ fun <R> CoroutineScope.subscribeMessages(
fun <R> CoroutineScope.subscribeGroupMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>.() -> R
listeners: GroupMessageSubscribersBuilder.() -> R
): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>(Unit) { filter, listener ->
return GroupMessageSubscribersBuilder(Unit) { filter, listener ->
subscribeAlways(coroutineContext, concurrencyKind) {
val toString = this.message.toString()
if (filter(this, toString))
@ -86,6 +89,8 @@ fun <R> CoroutineScope.subscribeGroupMessages(
}.run(listeners)
}
typealias FriendMessageSubscribersBuilder = MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>
/**
* 订阅来自所有 [Bot] 的所有好友消息事件
*
@ -95,12 +100,12 @@ fun <R> CoroutineScope.subscribeGroupMessages(
fun <R> CoroutineScope.subscribeFriendMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>.() -> R
listeners: FriendMessageSubscribersBuilder.() -> R
): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>(Unit) { filter, listener ->
return FriendMessageSubscribersBuilder(Unit) { filter, listener ->
subscribeAlways(coroutineContext, concurrencyKind) {
val toString = this.message.toString()
if (filter(this, toString))
@ -118,12 +123,12 @@ fun <R> CoroutineScope.subscribeFriendMessages(
fun <R> Bot.subscribeMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>.() -> R
listeners: MessagePacketSubscribersBuilder.() -> R
): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<MessagePacket<*, *>, Listener<MessagePacket<*, *>>, Unit, Unit>(Unit) { filter, listener ->
return MessagePacketSubscribersBuilder(Unit) { filter, listener ->
this.subscribeAlways(coroutineContext, concurrencyKind) {
val toString = this.message.toString()
if (filter(this, toString))
@ -143,12 +148,12 @@ fun <R> Bot.subscribeMessages(
fun <R> Bot.subscribeGroupMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>.() -> R
listeners: GroupMessageSubscribersBuilder.() -> R
): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<GroupMessage, Listener<GroupMessage>, Unit, Unit>(Unit) { filter, listener ->
return GroupMessageSubscribersBuilder(Unit) { filter, listener ->
this.subscribeAlways(coroutineContext, concurrencyKind) {
val toString = this.message.toString()
if (filter(this, toString))
@ -166,12 +171,12 @@ fun <R> Bot.subscribeGroupMessages(
fun <R> Bot.subscribeFriendMessages(
coroutineContext: CoroutineContext = EmptyCoroutineContext,
concurrencyKind: Listener.ConcurrencyKind = Listener.ConcurrencyKind.CONCURRENT,
listeners: MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>.() -> R
listeners: FriendMessageSubscribersBuilder.() -> R
): R {
contract {
callsInPlace(listeners, InvocationKind.EXACTLY_ONCE)
}
return MessageSubscribersBuilder<FriendMessage, Listener<FriendMessage>, Unit, Unit>(Unit) { filter, listener ->
return FriendMessageSubscribersBuilder(Unit) { filter, listener ->
this.subscribeAlways(coroutineContext, concurrencyKind) {
val toString = this.message.toString()
if (filter(this, toString))
@ -245,10 +250,6 @@ inline fun <reified E : BotEvent> Bot.incoming(
typealias MessageListener<T, R> = @MessageDsl suspend T.(String) -> R
// TODO: 2020/3/21 抽象消息构造器, 以在 select 时不可访问 `reply` 等函数
// TODO: 2020/3/21 引入 TypeAlias 或新的类型以缩短类型参数
/**
* 消息订阅构造器
*
@ -269,41 +270,47 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
*/
val subscriber: (M.(String) -> Boolean, MessageListener<M, RR>) -> Ret
) {
@Suppress("DEPRECATION")
open fun newListeningFilter(filter: M.(String) -> Boolean): ListeningFilter = ListeningFilter(filter)
/**
* 监听的条件
*/
open inner class ListeningFilter(
open inner class ListeningFilter @Deprecated(
"use newListeningFilter instead",
ReplaceWith("newListeningFilter(filter)")
) constructor(
val filter: M.(String) -> Boolean
) {
/**
* 进行逻辑 `or`.
*/
infix fun or(another: ListeningFilter): ListeningFilter =
ListeningFilter { filter.invoke(this, it) || another.filter.invoke(this, it) }
newListeningFilter { 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) }
newListeningFilter { 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) }
newListeningFilter { 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) }
newListeningFilter { !filter.invoke(this, it) || !another.filter.invoke(this, it) }
/**
* 进行逻辑 `not`
*/
fun not(): ListeningFilter =
ListeningFilter { !filter.invoke(this, it) }
newListeningFilter { !filter.invoke(this, it) }
/**
* 启动事件监听.
@ -312,35 +319,41 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
operator fun invoke(onEvent: MessageListener<M, R>): Ret {
return content(filter, onEvent)
}
}
infix fun reply(toReply: String): Ret {
return content(filter) { reply(toReply);stub }
@SinceMirai("0.29.0")
open infix fun ListeningFilter.reply(toReply: String): Ret {
return content(filter) { reply(toReply);stub }
}
@SinceMirai("0.29.0")
open infix fun ListeningFilter.reply(message: Message): Ret {
return content(filter) { reply(message);stub }
}
@SinceMirai("0.29.0")
open infix fun ListeningFilter.reply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
return content(filter) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
executeAndReply(replier)
}
}
infix fun reply(message: Message): Ret {
return content(filter) { reply(message);stub }
}
@SinceMirai("0.29.0")
open infix fun ListeningFilter.quoteReply(toReply: String): Ret {
return content(filter) { quoteReply(toReply);stub }
}
infix fun reply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
return content(filter) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
executeAndReply(replier)
}
}
@SinceMirai("0.29.0")
open infix fun ListeningFilter.quoteReply(message: Message): Ret {
return content(filter) { quoteReply(message);stub }
}
infix fun quoteReply(toReply: String): Ret {
return content(filter) { quoteReply(toReply);stub }
}
infix fun quoteReply(message: Message): Ret {
return content(filter) { quoteReply(message);stub }
}
infix fun quoteReply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
return content(filter) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
executeAndQuoteReply(replier)
}
@SinceMirai("0.29.0")
open infix fun ListeningFilter.quoteReply(replier: (@MessageDsl suspend M.(String) -> Any?)): Ret {
return content(filter) {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
executeAndQuoteReply(replier)
}
}
@ -350,7 +363,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* 无任何触发条件.
*/
@MessageDsl
fun always(onEvent: MessageListener<M, RR>): Ret = subscriber({ true }, onEvent)
open fun always(onEvent: MessageListener<M, RR>): Ret = subscriber({ true }, onEvent)
/**
* 如果消息内容 `==` [equals]
@ -372,7 +385,6 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
/**
* 如果消息内容 `==` [equals]
*/
@MiraiExperimentalAPI
@MessageDsl
@JvmName("case1")
@JsName("case1")
@ -603,7 +615,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* 如果是好友发来的消息
*/
@MessageDsl
fun sentByFriend(): ListeningFilter = ListeningFilter { this is FriendMessage }
fun sentByFriend(): ListeningFilter = newListeningFilter { this is FriendMessage }
/**
* 如果是管理员或群主发的消息
@ -682,7 +694,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
*/
@MessageDsl
fun content(filter: M.(String) -> Boolean): ListeningFilter =
ListeningFilter(filter)
newListeningFilter(filter)
/**
* 如果 [filter] 返回 `true` 就执行 `onEvent`
@ -741,7 +753,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* 若消息内容包含 [this] 则回复 [reply]
*/
@MessageDsl
infix fun String.containsReply(reply: String): Ret =
open infix fun String.containsReply(reply: String): Ret =
content({ this@containsReply in it }, { reply(reply); stub })
/**
@ -752,7 +764,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
infix fun String.containsReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret =
open infix fun String.containsReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret =
content({ this@containsReply in it }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
this.executeAndReply(replier)
@ -766,7 +778,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
infix fun Regex.matchingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
open infix fun Regex.matchingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
always {
val find = this@matchingReply.matchEntire(it) ?: return@always stub
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
@ -783,7 +795,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
infix fun Regex.findingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
open infix fun Regex.findingReply(replier: @MessageDsl suspend M.(MatchResult) -> Any?): Ret =
always {
val find = this@findingReply.find(it) ?: return@always stub
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
@ -806,7 +818,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他类型则 [Any.toString] 后回复
*/
@MessageDsl
infix fun String.startsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
open infix fun String.startsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
val toCheck = this.trimStart()
return content({ it.trim().startsWith(toCheck) }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
@ -830,7 +842,7 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
* @param replier 若返回 [Message] 则直接发送; 若返回 [Unit] 则不回复; 其他情况则 [Any.toString] 后回复
*/
@MessageDsl
infix fun String.endsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
open infix fun String.endsWithReply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
val toCheck = this.trimEnd()
return content({ it.trim().endsWith(toCheck) }, {
@Suppress("DSL_SCOPE_VIOLATION_WARNING")
@ -841,19 +853,19 @@ open class MessageSubscribersBuilder<M : MessagePacket<*, *>, out Ret, R : RR, R
}
@MessageDsl
infix fun String.reply(reply: String): Ret {
open infix fun String.reply(reply: String): Ret {
val toCheck = this.trim()
return content({ it.trim() == toCheck }, { reply(reply);stub })
}
@MessageDsl
infix fun String.reply(reply: Message): Ret {
open infix fun String.reply(reply: Message): Ret {
val toCheck = this.trim()
return content({ it.trim() == toCheck }, { reply(reply);stub })
}
@MessageDsl
infix fun String.reply(replier: @MessageDsl suspend M.(String) -> Any?): Ret {
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")