Support distinction for MessageScope

This commit is contained in:
Him188 2020-08-31 12:55:01 +08:00
parent 2dd808c0d7
commit 8db2cd8e8f
2 changed files with 126 additions and 8 deletions

View File

@ -33,6 +33,7 @@ import net.mamoe.mirai.console.internal.command.qualifiedNameOrTip
import net.mamoe.mirai.console.internal.data.castOrNull import net.mamoe.mirai.console.internal.data.castOrNull
import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf import net.mamoe.mirai.console.internal.plugin.rootCauseOrSelf
import net.mamoe.mirai.console.util.ConsoleExperimentalAPI import net.mamoe.mirai.console.util.ConsoleExperimentalAPI
import net.mamoe.mirai.console.util.MessageScope
import net.mamoe.mirai.console.util.childScope import net.mamoe.mirai.console.util.childScope
import net.mamoe.mirai.console.util.childScopeContext import net.mamoe.mirai.console.util.childScopeContext
import net.mamoe.mirai.contact.* import net.mamoe.mirai.contact.*
@ -121,6 +122,11 @@ import kotlin.internal.LowPriorityInOverloadResolution
* +-----------------------------+----------------------------+---------------+ * +-----------------------------+----------------------------+---------------+
* ``` * ```
* *
* ## Scoping: [MessageScope]
* 在处理多个消息对象时, 可通过 [MessageScope] 简化操作.
*
* 查看 [MessageScope] 以获取更多信息.
*
* @see ConsoleCommandSender 控制台 * @see ConsoleCommandSender 控制台
* @see UserCommandSender [User] ([群成员][Member], [好友][Friend]) * @see UserCommandSender [User] ([群成员][Member], [好友][Friend])
* @see toCommandSender * @see toCommandSender

View File

@ -20,26 +20,111 @@ import kotlinx.coroutines.flow.fold
import net.mamoe.kjbb.JvmBlockingBridge import net.mamoe.kjbb.JvmBlockingBridge
import net.mamoe.mirai.console.command.CommandSender import net.mamoe.mirai.console.command.CommandSender
import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.contact.Contact
import net.mamoe.mirai.contact.User
import net.mamoe.mirai.message.data.Message import net.mamoe.mirai.message.data.Message
import kotlin.internal.InlineOnly import kotlin.internal.InlineOnly
import kotlin.internal.LowPriorityInOverloadResolution import kotlin.internal.LowPriorityInOverloadResolution
@ConsoleExperimentalAPI /**
* 表示几个消息对象的 '域', 即消息对象的集合. 用于最小化将同一条消息发送给多个类型不同的目标的付出.
*
* ## 支持的消息对象类型
* [Contact], [CommandSender], [MessageScope] (递归).
*
* 在下文, `A` `B` 指代这三种类型的其中两种, 允许排列组合. `A.scopeWith(B)` 可能表示 `Contact.scopeWith(MessageScope)`.
*
* ## 获得 [MessageScope]
* - `A.asMessageScope()`.
* - `C<A>.toMessageScope()`. 其中 `C` 表示 `Iterable`, `Sequence`, `Flow`, `Array` 其中任一.
*
* ## 连接 [MessageScope]
* - `A.scopeWith(vararg B)`.
* - `A.scopeWith(vararg A)`.
* - `A.scopeWithNotNull(vararg B?)`. 类似 [listOfNotNull].
* - `A.scopeWithNotNull(vararg A?)`. 类似 [listOfNotNull].
*
* ## 自动去重
* 在连接时, [MessageScope] 会自动根据真实的 [收信对象][CommandSender.subject] 去重.
*
* `member.asCommandSender().scopeWith(member.group)`,
* 返回的 [MessageScope] 实际上只包含 `member.group`. 因为 `member.asCommandSender()` 的最终收信对象就是 `member.group`.
*
* 因此在使用 [scopeWith] , 无需考虑重复性, 只需要把希望发送的目标全部列入.
*
* ## 使用 [MessageScope]
* `scopeWith` `scopeWithNotNull` 后加 `lambda` 参数即可表示使用 [MessageScope].
* :
* ```
* A.scopeWith(B) { // this: MessageScope
* sendMessage(...)
* }
* ```
*
* ## 典例
* 在处理指令时, 目标群对象可能与发件人群对象不同, 如用户在 A 群发指令, 以禁言 B 群的成员.
* 此时机器人可能需要同时广播通知到 A 群和 B .
*
* 由于 [CommandSender] [Contact] 无公共接口, 无法使用 [listOfNotNull] 遍历处理. [MessageScope] 就是设计为解决这样的问题.
*
* ```
* // 在一个 CompositeCommand 内
* @Handler
* suspend fun CommandSender.handle(target: Member) {
* val duration = Random.nextInt(1, 15)
* target.mute(duration)
*
* // 不使用 MessageScope, 无用的样板代码
* val thisGroup = this.getGroupOrNull()
* val message = "${this.name} 禁言 ${target.nameCardOrNick} $duration"
* if (target.group != thisGroup) {
* target.group.sendMessage(message)
* }
* sendMessage(message)
*
* // 使用 MessageScope, 清晰逻辑
* // 表示至少发送给 `this`, 当 `this` 的真实发信对象与 `target.group` 不同时, 还额外发送给 `target.group`
* this.scopeWithNotNull(target.group) {
* sendMessage("${name} 禁言了 ${target.nameCardOrNick} $duration")
* }
*
* // 同样地, 可以扩展用法, 同时私聊指令执行者:
* // this.scopeWithNotNull(
* // target,
* // target.group
* // ) { ... }
* }
* ```
*/
public interface MessageScope { public interface MessageScope {
/** /**
* 立刻发送一条消息. * 如果此 [MessageScope], 仅包含一个消息对象, [realTarget] 指向这个对象.
*
* 对于 [CommandSender] 作为 [MessageScope], [realTarget] 总是指令执行者 [User], [CommandSender.user]
*
* [realTarget] 用于 [MessageScope.invoke] 时的去重.
*
* @suppress API 不稳定, 可能在任何时间被修改
*/
@ConsoleExperimentalAPI
public val realTarget: Any?
/**
* 立刻以此发送消息给所有在此 [MessageScope] 下的消息对象
*/ */
@JvmBlockingBridge @JvmBlockingBridge
public suspend fun sendMessage(message: Message) public suspend fun sendMessage(message: Message)
/** /**
* 立刻发送一条消息. * 立刻以此发送消息给所有在此 [MessageScope] 下的消息对象
*/ */
@JvmDefault @JvmDefault
@JvmBlockingBridge @JvmBlockingBridge
public suspend fun sendMessage(message: String) public suspend fun sendMessage(message: String)
} }
/**
* 使用 [MessageScope] 里的所有消息对象. [kotlin.run] 相同.
*/
@JvmSynthetic @JvmSynthetic
public inline operator fun <R, MS : MessageScope> MS.invoke(action: MS.() -> R): R = this.action() public inline operator fun <R, MS : MessageScope> MS.invoke(action: MS.() -> R): R = this.action()
@ -47,6 +132,10 @@ public inline operator fun <R, MS : MessageScope> MS.invoke(action: MS.() -> R):
// Builders // Builders
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
/*
* 实现提示: 以下所有代码都通过 codegen 模块中 net.mamoe.mirai.console.codegen.MessageScopeCodegen 生成. 请不要手动修改它们.
*/
//// region MessageScopeBuilders CODEGEN //// //// region MessageScopeBuilders CODEGEN ////
public fun Contact.asMessageScope(): MessageScope = createScopeDelegate(this) public fun Contact.asMessageScope(): MessageScope = createScopeDelegate(this)
@ -531,7 +620,7 @@ public suspend fun Flow<MessageScope>.toMessageScope(): MessageScope { // Flow<A
// Internals // Internals
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
// these three are for codegen // [MessageScope] 实现
@PublishedApi @PublishedApi
@InlineOnly @InlineOnly
@ -552,24 +641,41 @@ private inline fun createScopeDelegate(o: CommandSender) = CommandSenderAsMessag
@InlineOnly @InlineOnly
private inline fun createScopeDelegate(o: Contact) = ContactAsMessageScope(o) private inline fun createScopeDelegate(o: Contact) = ContactAsMessageScope(o)
private fun MessageScope.asSequence(): Sequence<MessageScope> {
return if (this is CombinedScope) {
sequenceOf(this.first.asSequence(), this.second.asSequence()).flatten()
} else sequenceOf(this)
}
private class CombinedScope( private class CombinedScope(
private val first: MessageScope, private val first: MessageScope,
private val second: MessageScope private val second: MessageScope
) : MessageScope { ) : MessageScope {
override val realTarget: Any? get() = null
private val targets: List<MessageScope> by lazy {
this.asSequence().distinctBy { it.realTarget }.toList()
}
override suspend fun sendMessage(message: Message) { override suspend fun sendMessage(message: Message) {
first.sendMessage(message) for (target in targets) {
second.sendMessage(message) target.sendMessage(message)
}
} }
override suspend fun sendMessage(message: String) { override suspend fun sendMessage(message: String) {
first.sendMessage(message) for (target in targets) {
second.sendMessage(message) target.sendMessage(message)
}
} }
} }
private class CommandSenderAsMessageScope( private class CommandSenderAsMessageScope(
private val sender: CommandSender private val sender: CommandSender
) : MessageScope { ) : MessageScope {
override val realTarget: Any?
get() = sender.user ?: sender // ConsoleCommandSender
override suspend fun sendMessage(message: Message) { override suspend fun sendMessage(message: Message) {
sender.sendMessage(message) sender.sendMessage(message)
} }
@ -582,6 +688,9 @@ private class CommandSenderAsMessageScope(
private class ContactAsMessageScope( private class ContactAsMessageScope(
private val sender: Contact private val sender: Contact
) : MessageScope { ) : MessageScope {
override val realTarget: Any?
get() = sender
override suspend fun sendMessage(message: Message) { override suspend fun sendMessage(message: Message) {
sender.sendMessage(message) sender.sendMessage(message)
} }
@ -592,6 +701,9 @@ private class ContactAsMessageScope(
} }
private object NoopMessageScope : MessageScope { private object NoopMessageScope : MessageScope {
override val realTarget: Any?
get() = null
override suspend fun sendMessage(message: Message) { override suspend fun sendMessage(message: Message) {
} }