mirror of
https://github.com/mamoe/mirai.git
synced 2025-01-10 18:40:15 +08:00
Support distinction for MessageScope
This commit is contained in:
parent
2dd808c0d7
commit
8db2cd8e8f
@ -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.plugin.rootCauseOrSelf
|
||||
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.childScopeContext
|
||||
import net.mamoe.mirai.contact.*
|
||||
@ -121,6 +122,11 @@ import kotlin.internal.LowPriorityInOverloadResolution
|
||||
* +-----------------------------+----------------------------+---------------+
|
||||
* ```
|
||||
*
|
||||
* ## Scoping: [MessageScope]
|
||||
* 在处理多个消息对象时, 可通过 [MessageScope] 简化操作.
|
||||
*
|
||||
* 查看 [MessageScope] 以获取更多信息.
|
||||
*
|
||||
* @see ConsoleCommandSender 控制台
|
||||
* @see UserCommandSender [User] ([群成员][Member], [好友][Friend])
|
||||
* @see toCommandSender
|
||||
|
@ -20,26 +20,111 @@ import kotlinx.coroutines.flow.fold
|
||||
import net.mamoe.kjbb.JvmBlockingBridge
|
||||
import net.mamoe.mirai.console.command.CommandSender
|
||||
import net.mamoe.mirai.contact.Contact
|
||||
import net.mamoe.mirai.contact.User
|
||||
import net.mamoe.mirai.message.data.Message
|
||||
import kotlin.internal.InlineOnly
|
||||
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 {
|
||||
/**
|
||||
* 立刻发送一条消息.
|
||||
* 如果此 [MessageScope], 仅包含一个消息对象, 则 [realTarget] 指向这个对象.
|
||||
*
|
||||
* 对于 [CommandSender] 作为 [MessageScope], [realTarget] 总是指令执行者 [User], 即 [CommandSender.user]
|
||||
*
|
||||
* [realTarget] 用于 [MessageScope.invoke] 时的去重.
|
||||
*
|
||||
* @suppress 此 API 不稳定, 可能在任何时间被修改
|
||||
*/
|
||||
@ConsoleExperimentalAPI
|
||||
public val realTarget: Any?
|
||||
|
||||
/**
|
||||
* 立刻以此发送消息给所有在此 [MessageScope] 下的消息对象
|
||||
*/
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendMessage(message: Message)
|
||||
|
||||
/**
|
||||
* 立刻发送一条消息.
|
||||
* 立刻以此发送消息给所有在此 [MessageScope] 下的消息对象
|
||||
*/
|
||||
@JvmDefault
|
||||
@JvmBlockingBridge
|
||||
public suspend fun sendMessage(message: String)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 [MessageScope] 里的所有消息对象. 与 [kotlin.run] 相同.
|
||||
*/
|
||||
@JvmSynthetic
|
||||
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
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
* 实现提示: 以下所有代码都通过 codegen 模块中 net.mamoe.mirai.console.codegen.MessageScopeCodegen 生成. 请不要手动修改它们.
|
||||
*/
|
||||
|
||||
//// region MessageScopeBuilders CODEGEN ////
|
||||
|
||||
public fun Contact.asMessageScope(): MessageScope = createScopeDelegate(this)
|
||||
@ -531,7 +620,7 @@ public suspend fun Flow<MessageScope>.toMessageScope(): MessageScope { // Flow<A
|
||||
// Internals
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// these three are for codegen
|
||||
// [MessageScope] 实现
|
||||
|
||||
@PublishedApi
|
||||
@InlineOnly
|
||||
@ -552,24 +641,41 @@ private inline fun createScopeDelegate(o: CommandSender) = CommandSenderAsMessag
|
||||
@InlineOnly
|
||||
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 val first: MessageScope,
|
||||
private val second: 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) {
|
||||
first.sendMessage(message)
|
||||
second.sendMessage(message)
|
||||
for (target in targets) {
|
||||
target.sendMessage(message)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun sendMessage(message: String) {
|
||||
first.sendMessage(message)
|
||||
second.sendMessage(message)
|
||||
for (target in targets) {
|
||||
target.sendMessage(message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class CommandSenderAsMessageScope(
|
||||
private val sender: CommandSender
|
||||
) : MessageScope {
|
||||
override val realTarget: Any?
|
||||
get() = sender.user ?: sender // ConsoleCommandSender
|
||||
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
sender.sendMessage(message)
|
||||
}
|
||||
@ -582,6 +688,9 @@ private class CommandSenderAsMessageScope(
|
||||
private class ContactAsMessageScope(
|
||||
private val sender: Contact
|
||||
) : MessageScope {
|
||||
override val realTarget: Any?
|
||||
get() = sender
|
||||
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
sender.sendMessage(message)
|
||||
}
|
||||
@ -592,6 +701,9 @@ private class ContactAsMessageScope(
|
||||
}
|
||||
|
||||
private object NoopMessageScope : MessageScope {
|
||||
override val realTarget: Any?
|
||||
get() = null
|
||||
|
||||
override suspend fun sendMessage(message: Message) {
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user