[Review] MessageSourceBuilder

This commit is contained in:
Him188 2021-01-09 21:36:27 +08:00
parent 3e4d279bcc
commit 1a1eb0e2a4
4 changed files with 99 additions and 148 deletions

View File

@ -59,6 +59,12 @@ public interface IMirai : LowLevelApiAccessor {
@MiraiInternalApi
public val Http: HttpClient
public fun getUin(contactOrBot: ContactOrBot): Long {
return if (contactOrBot is Group)
calculateGroupUinByGroupCode(contactOrBot.id)
else contactOrBot.id
}
/**
* 使用 groupCode 计算 groupUin. 这两个值仅在 mirai 内部协议区分, 一般人使用时无需在意.
*/
@ -137,15 +143,11 @@ public interface IMirai : LowLevelApiAccessor {
*
* @param ids [MessageSource.ids]
* @param internalIds [MessageSource.internalIds]
*
* @param fromUin 为用户时为 [Friend.id], 为群时需使用 [IMirai.calculateGroupUinByGroupCode] 计算
* @param targetUin 为用户时为 [Friend.id], 为群时需使用 [IMirai.calculateGroupUinByGroupCode] 计算
*/
@MiraiExperimentalApi("This is very experimental and is subject to change.")
public fun constructMessageSource(
botId: Long,
kind: MessageSourceKind,
fromUin: Long, targetUin: Long,
fromId: Long, targetId: Long,
ids: IntArray, time: Int, internalIds: IntArray,
originalMessage: MessageChain
): OfflineMessageSource

View File

@ -66,7 +66,7 @@ import net.mamoe.mirai.utils.safeCast
* @see OnlineMessageSource 在线消息的 [MessageSource]
* @see OfflineMessageSource 离线消息的 [MessageSource]
*
* @see buildMessageSource 一个 [OfflineMessageSource]
* @see buildMessageSource 一个 [OfflineMessageSource]
*/
@Serializable(MessageSource.Serializer::class)
public sealed class MessageSource : Message, MessageMetadata, ConstrainSingle {
@ -445,6 +445,8 @@ public sealed class OnlineMessageSource : MessageSource() {
* 此消息源可能来自一条与机器人无关的消息. 因此无法提供对象化的 `sender` `target` 获取.
*
* @see buildMessageSource 构建一个 [OfflineMessageSource]
* @see IMirai.constructMessageSource
* @see OnlineMessageSource.toOffline
*/
public abstract class OfflineMessageSource : MessageSource() {
public companion object Key :

View File

@ -1,5 +1,5 @@
/*
* Copyright 2019-2020 Mamoe Technologies and contributors.
* Copyright 2019-2021 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.
@ -14,14 +14,12 @@
package net.mamoe.mirai.message.data
import net.mamoe.mirai.Bot
import net.mamoe.mirai.IMirai
import net.mamoe.mirai.Mirai
import net.mamoe.mirai.contact.*
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutFriend
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutGroup
import net.mamoe.mirai.message.data.MessageSource.Key.isAboutTemp
import net.mamoe.mirai.contact.ContactOrBot
import net.mamoe.mirai.message.data.MessageSource.Key.quote
import net.mamoe.mirai.message.data.MessageSource.Key.recall
import net.mamoe.mirai.utils.MiraiExperimentalApi
import net.mamoe.mirai.message.data.MessageSourceBuilder.Companion.create
import net.mamoe.mirai.utils.currentTimeSeconds
/**
@ -29,7 +27,7 @@ import net.mamoe.mirai.utils.currentTimeSeconds
*/
@JvmName("toOfflineMessageSource")
public fun OnlineMessageSource.toOffline(): OfflineMessageSource =
OfflineMessageSourceByOnline(this)
Mirai.constructMessageSource(botId, kind, fromId, targetId, ids, time, internalIds, originalMessage)
///////////////
//// AMEND ////
@ -41,24 +39,27 @@ public fun OnlineMessageSource.toOffline(): OfflineMessageSource =
*
* @see buildMessageSource 查看更多说明
*/
@MiraiExperimentalApi
@JvmName("copySource")
public fun MessageSource.copyAmend(
block: MessageSourceAmender.() -> Unit
): OfflineMessageSource = toMutableOffline().apply(block)
): OfflineMessageSource = MessageSourceAmender(this).apply(block).run {
Mirai.constructMessageSource(botId, kind, fromId, targetId, ids, time, internalIds, originalMessage)
}
/**
* 仅于 [copyAmend] 中修改 [MessageSource]
*/
public interface MessageSourceAmender {
public var kind: MessageSourceKind
public var fromUin: Long
public var targetUin: Long
public var ids: IntArray
public var time: Int
public var internalIds: IntArray
public class MessageSourceAmender internal constructor(
origin: MessageSource,
) : MessageSourceBuilder() {
public var kind: MessageSourceKind = origin.kind
public var originalMessage: MessageChain = origin.originalMessage
public var originalMessage: MessageChain
public override var fromId: Long = origin.fromId
public override var targetId: Long = origin.targetId
public override var ids: IntArray = origin.ids
public override var time: Int = origin.time
public override var internalIds: IntArray = origin.internalIds
/** 从另一个 [MessageSource] 中复制 [ids], [internalIds], [time]*/
public fun metadataFrom(another: MessageSource) {
@ -78,8 +79,8 @@ public interface MessageSourceAmender {
* 构建一个 [OfflineMessageSource]
*
* ### 参数
* 一个 [OfflineMessageSource] 要以下参数:
* - 发送人和发送目标: 通过 [MessageSourceBuilder.sendTo] 设置
* 一个 [OfflineMessageSource] 要以下参数:
* - 发送人和发送目标: 通过 [MessageSourceBuilder.sender], [MessageSourceBuilder.target] 设置
* - 消息元数据 ( [MessageSource.ids], [MessageSource.internalIds], [MessageSource.time])
* 元数据用于 [撤回][MessageSource.recall], [引用回复][MessageSource.quote], 和官方客户端定位原消息.
* 可通过 [MessageSourceBuilder.ids], [MessageSourceBuilder.time], [MessageSourceBuilder.internalIds] 设置
@ -92,8 +93,9 @@ public interface MessageSourceAmender {
*
* ### 实例
* ```
* bot.buildMessageSource {
* bot sendTo target // 指定发送人和发送目标
* bot.buildMessageSource(MessageSourceKind.GROUP) {
* from(bot)
* target(target)
* metadata(source) // 从另一个消息源复制 ids, internalIds, time
*
* messages { // 指定消息内容
@ -101,40 +103,49 @@ public interface MessageSourceAmender {
* }
* }
* ```
*
* @see copyAmend
*/
@JvmSynthetic
@MiraiExperimentalApi
public fun Bot.buildMessageSource(block: MessageSourceBuilder.() -> Unit): MessageSource {
val builder = MessageSourceBuilderImpl().apply(block)
return Mirai.constructMessageSource(
this.id,
builder.kind ?: error("You must call `Contact.sendTo(Contact)` when `buildMessageSource`"),
builder.fromUin,
builder.targetUin,
builder.ids,
builder.time,
builder.internalIds,
builder.originalMessages.build()
)
public fun IMirai.buildMessageSource(
botId: Long,
kind: MessageSourceKind,
block: MessageSourceBuilder.() -> Unit
): OfflineMessageSource = MessageSourceBuilder.create().apply(block).run {
Mirai.constructMessageSource(botId, kind, fromId, targetId, ids, time, internalIds, originalMessages.build())
}
/**
* 构建一个 [OfflineMessageSource]
*
* @see buildMessageSource
*/
public abstract class MessageSourceBuilder {
internal abstract var kind: MessageSourceKind?
internal abstract var fromUin: Long
internal abstract var targetUin: Long
public fun Bot.buildMessageSource(
kind: MessageSourceKind,
block: MessageSourceBuilder.() -> Unit
): OfflineMessageSource = Mirai.buildMessageSource(this.id, kind, block)
internal abstract var ids: IntArray
internal abstract var time: Int
internal abstract var internalIds: IntArray
/**
* @see buildMessageSource
* @see create
*/
public open class MessageSourceBuilder internal constructor() {
public open var fromId: Long = 0
public open var targetId: Long = 0
public open var ids: IntArray = intArrayOf()
/**
* seconds
* @see MessageSource.time
*/
public open var time: Int = currentTimeSeconds().toInt()
public open var internalIds: IntArray = intArrayOf()
@PublishedApi
internal val originalMessages: MessageChainBuilder = MessageChainBuilder()
public fun time(from: MessageSource): MessageSourceBuilder = apply { this.time = from.time }
public val now: Int get() = currentTimeSeconds().toInt()
public fun time(value: Int): MessageSourceBuilder = apply { this.time = value }
public fun internalId(from: MessageSource): MessageSourceBuilder = apply { this.internalIds = from.internalIds }
@ -158,11 +169,10 @@ public abstract class MessageSourceBuilder {
* 从另一个 [MessageSource] 复制所有信息, 包括消息内容. 不会清空已有消息.
*/
public fun allFrom(source: MessageSource): MessageSourceBuilder {
this.kind = determineKind(source)
this.ids = source.ids
this.time = source.time
this.fromUin = source.fromId
this.targetUin = source.targetId
this.fromId = source.fromId
this.targetId = source.targetId
this.internalIds = source.internalIds
this.originalMessages.addAll(source.originalMessage)
return this
@ -194,101 +204,38 @@ public abstract class MessageSourceBuilder {
public fun clearMessages(): MessageSourceBuilder = apply { this.originalMessages.clear() }
/**
* 设置 [发送人][this] [发送目标][target], 并自动判断 [kind]
* 设置发信人
*/
@JvmSynthetic
public abstract infix fun ContactOrBot.sendTo(target: ContactOrBot): MessageSourceBuilder
public fun sender(sender: ContactOrBot): MessageSourceBuilder = apply {
this.fromId = Mirai.getUin(sender)
}
/**
* @see IMirai.getUin
*/
public fun sender(uin: Long): MessageSourceBuilder = apply {
this.fromId = uin
}
/**
* 设置发信目标
*/
public fun target(target: ContactOrBot): MessageSourceBuilder = apply {
this.targetId = Mirai.getUin(target)
}
/**
* @see IMirai.getUin
*/
public fun target(uin: Long): MessageSourceBuilder = apply {
this.targetId = uin
}
public fun setSenderAndTarget(sender: ContactOrBot, target: ContactOrBot): MessageSourceBuilder =
sender sendTo target
}
sender(sender).target(target)
//////////////////
//// INTERNAL ////
//////////////////
internal class MessageSourceBuilderImpl : MessageSourceBuilder() {
override var kind: MessageSourceKind? = null
override var fromUin: Long = 0
override var targetUin: Long = 0
override var ids: IntArray = intArrayOf()
override var time: Int = currentTimeSeconds().toInt()
override var internalIds: IntArray = intArrayOf()
@JvmSynthetic
override fun ContactOrBot.sendTo(target: ContactOrBot): MessageSourceBuilder {
fromUin = if (this is Group) {
Mirai.calculateGroupUinByGroupCode(this.id)
} else this.id
targetUin = if (target is Group) {
Mirai.calculateGroupUinByGroupCode(target.id)
} else target.id
check(this != target) { "sender and target mustn't be the same" }
kind = when {
this is Group || target is Group -> MessageSourceKind.GROUP
this is Member || target is Member -> MessageSourceKind.TEMP
this is Bot && target is Friend -> MessageSourceKind.FRIEND
this is Friend && target is Bot -> MessageSourceKind.FRIEND
this is Stranger || target is Stranger -> MessageSourceKind.STRANGER
else -> throw IllegalArgumentException("Cannot determine source kind for sender $this and target $target")
}
return this@MessageSourceBuilderImpl
public companion object {
@JvmStatic
public fun create(): MessageSourceBuilder = MessageSourceBuilder()
}
}
@JvmSynthetic
internal fun MessageSource.toMutableOffline(): MutableOfflineMessageSourceByOnline =
MutableOfflineMessageSourceByOnline(this)
internal class MutableOfflineMessageSourceByOnline(
origin: MessageSource
) : OfflineMessageSource(), MessageSourceAmender {
override var kind: MessageSourceKind = determineKind(origin)
override var fromUin: Long
get() = fromId
set(value) {
fromId = value
}
override var targetUin: Long
get() = targetId
set(value) {
targetId = value
}
override val botId: Long = origin.botId
override var ids: IntArray = origin.ids
override var internalIds: IntArray = origin.internalIds
override var time: Int = origin.time
override var fromId: Long = origin.fromId
override var targetId: Long = origin.targetId
override var originalMessage: MessageChain = origin.originalMessage
}
private fun determineKind(source: MessageSource): MessageSourceKind {
return when {
source.isAboutGroup() -> MessageSourceKind.GROUP
source.isAboutFriend() -> MessageSourceKind.FRIEND
source.isAboutTemp() -> MessageSourceKind.TEMP
else -> error("stub")
}
}
internal class OfflineMessageSourceByOnline(
private val onlineMessageSource: OnlineMessageSource
) : OfflineMessageSource() {
override val kind: MessageSourceKind
get() = onlineMessageSource.kind
override val botId: Long get() = onlineMessageSource.botId
override val ids: IntArray get() = onlineMessageSource.ids
override val internalIds: IntArray get() = onlineMessageSource.internalIds
override val time: Int get() = onlineMessageSource.time
override val fromId: Long get() = onlineMessageSource.fromId
override val targetId: Long get() = onlineMessageSource.targetId
override val originalMessage: MessageChain get() = onlineMessageSource.originalMessage
}
}

View File

@ -892,14 +892,14 @@ internal open class MiraiImpl : IMirai, LowLevelApiAccessor {
override fun constructMessageSource(
botId: Long,
kind: MessageSourceKind,
fromUin: Long,
targetUin: Long,
fromId: Long,
targetId: Long,
ids: IntArray,
time: Int,
internalIds: IntArray,
originalMessage: MessageChain
): OfflineMessageSource = OfflineMessageSourceImplData(
kind, ids, botId, time, fromUin, targetUin, originalMessage, internalIds
kind, ids, botId, time, fromId, targetId, originalMessage, internalIds
)
}