Merge remote-tracking branch 'origin/master'

This commit is contained in:
jiahua.liu 2020-02-23 20:09:16 +08:00
commit 014439e731
21 changed files with 189 additions and 143 deletions

View File

@ -17,6 +17,7 @@ import net.mamoe.mirai.message.FriendMessage
import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.GroupMessage
import net.mamoe.mirai.message.MessagePacket import net.mamoe.mirai.message.MessagePacket
import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.data.*
import net.mamoe.mirai.utils.MiraiExperimentalAPI
import net.mamoe.mirai.utils.MiraiInternalAPI import net.mamoe.mirai.utils.MiraiInternalAPI
/* /*
@ -99,7 +100,7 @@ fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply
fun MessageChainDTO.toMessageChain(contact: Contact) = fun MessageChainDTO.toMessageChain(contact: Contact) =
buildMessageChain { this@toMessageChain.forEach { add(it.toMessage(contact)) } } buildMessageChain { this@toMessageChain.forEach { add(it.toMessage(contact)) } }
@UseExperimental(ExperimentalUnsignedTypes::class) @UseExperimental(ExperimentalUnsignedTypes::class, MiraiExperimentalAPI::class)
fun Message.toDTO() = when (this) { fun Message.toDTO() = when (this) {
is MessageSource -> MessageSourceDTO(id) is MessageSource -> MessageSourceDTO(id)
is At -> AtDTO(target, display) is At -> AtDTO(target, display)
@ -111,7 +112,7 @@ fun Message.toDTO() = when (this) {
else -> UnknownMessageDTO else -> UnknownMessageDTO
} }
@UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class, MiraiExperimentalAPI::class)
fun MessageDTO.toMessage(contact: Contact) = when (this) { fun MessageDTO.toMessage(contact: Contact) = when (this) {
is AtDTO -> At((contact as Group)[target]) is AtDTO -> At((contact as Group)[target])
is AtAllDTO -> AtAll is AtAllDTO -> AtAll

View File

@ -13,10 +13,7 @@ import net.mamoe.mirai.console.graphical.model.VerificationCodeModel
import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment import net.mamoe.mirai.console.graphical.view.VerificationCodeFragment
import net.mamoe.mirai.console.utils.MiraiConsoleUI import net.mamoe.mirai.console.utils.MiraiConsoleUI
import net.mamoe.mirai.utils.LoginSolver import net.mamoe.mirai.utils.LoginSolver
import tornadofx.Controller import tornadofx.*
import tornadofx.Scope
import tornadofx.find
import tornadofx.observableListOf
class MiraiGraphicalUIController : Controller(), MiraiConsoleUI { class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
@ -28,8 +25,6 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
val botList = observableListOf<BotModel>() val botList = observableListOf<BotModel>()
val pluginList: ObservableList<PluginModel> by lazy(::getPluginsFromConsole) val pluginList: ObservableList<PluginModel> by lazy(::getPluginsFromConsole)
// val consoleConfig : Map<String, Any> by lazy(::getConfigFromConsole)
val consoleInfo = ConsoleInfo() val consoleInfo = ConsoleInfo()
fun login(qq: String, psd: String) { fun login(qq: String, psd: String) {
@ -79,10 +74,9 @@ class MiraiGraphicalUIController : Controller(), MiraiConsoleUI {
override fun createLoginSolver(): LoginSolver = loginSolver override fun createLoginSolver(): LoginSolver = loginSolver
private fun getPluginsFromConsole(): ObservableList<PluginModel> { private fun getPluginsFromConsole(): ObservableList<PluginModel> =
// TODO MiraiConsole.pluginManager.getAllPluginDescriptions().map(::PluginModel).toObservable()
return observableListOf<PluginModel>()
}
} }
class GraphicalLoginSolver : LoginSolver() { class GraphicalLoginSolver : LoginSolver() {

View File

@ -3,16 +3,17 @@ package net.mamoe.mirai.console.graphical.model
import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject import com.jfoenix.controls.datamodels.treetable.RecursiveTreeObject
import javafx.beans.property.SimpleBooleanProperty import javafx.beans.property.SimpleBooleanProperty
import javafx.beans.property.SimpleStringProperty import javafx.beans.property.SimpleStringProperty
import net.mamoe.mirai.console.plugins.PluginDescription
import tornadofx.getValue import tornadofx.getValue
import tornadofx.setValue import tornadofx.setValue
class PluginModel : RecursiveTreeObject<PluginModel>() { class PluginModel(
val name: String,
val nameProperty = SimpleStringProperty(this, "nameProperty") val version: String,
val name by nameProperty val author: String,
val description: String
val descriptionProperty = SimpleStringProperty(this, "descriptionProperty") ) : RecursiveTreeObject<PluginModel>() {
val description by descriptionProperty constructor(plugin: PluginDescription):this(plugin.name, plugin.version, plugin.author, plugin.info)
val enabledProperty = SimpleBooleanProperty(this, "enabledProperty") val enabledProperty = SimpleBooleanProperty(this, "enabledProperty")
var enabled by enabledProperty var enabled by enabledProperty

View File

@ -13,10 +13,21 @@ class PluginsView : View() {
override val root = jfxTreeTableView(plugins) { override val root = jfxTreeTableView(plugins) {
columns.addAll( columns.addAll(
JFXTreeTableColumn<PluginModel, String>("插件名").apply { }, JFXTreeTableColumn<PluginModel, String>("插件名").apply {
JFXTreeTableColumn<PluginModel, String>("版本").apply { }, prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
JFXTreeTableColumn<PluginModel, String>("作者").apply { }, },
JFXTreeTableColumn<PluginModel, String>("介绍").apply { } JFXTreeTableColumn<PluginModel, String>("版本").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
},
JFXTreeTableColumn<PluginModel, String>("作者").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.1))
},
JFXTreeTableColumn<PluginModel, String>("介绍").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.6))
},
JFXTreeTableColumn<PluginModel, String>("操作").apply {
prefWidthProperty().bind(this@jfxTreeTableView.widthProperty().multiply(0.08))
}
) )
} }
} }

View File

@ -1,23 +1,36 @@
package net.mamoe.mirai.console.graphical.view package net.mamoe.mirai.console.graphical.view
import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController import net.mamoe.mirai.console.graphical.controller.MiraiGraphicalUIController
import net.mamoe.mirai.console.graphical.util.jfxButton
import net.mamoe.mirai.console.graphical.util.jfxTextfield import net.mamoe.mirai.console.graphical.util.jfxTextfield
import tornadofx.View import tornadofx.*
import tornadofx.field
import tornadofx.fieldset
import tornadofx.form
class SettingsView : View() { class SettingsView : View() {
private val controller = find<MiraiGraphicalUIController>() private val controller = find<MiraiGraphicalUIController>()
override val root = form { override val root = form {
// controller.consoleConfig.forEach {
// fieldset { fieldset {
// field(it.key) { field {
// jfxTextfield(it.value.toString()) { isEditable = false } jfxButton("撤掉") { }
// } jfxButton("保存") { }
// } }
// } }
fieldset("插件目录") {
field {
jfxTextfield("...") { isEditable = false }
jfxButton("打开目录")
}
}
fieldset("最大日志容量") {
field {
jfxTextfield("...") {
}
}
}
} }
} }

View File

@ -326,7 +326,7 @@ internal class NotOnlineImageFromServer(
internal fun MsgComm.Msg.toMessageChain(): MessageChain { internal fun MsgComm.Msg.toMessageChain(): MessageChain {
val elements = this.msgBody.richText.elems val elements = this.msgBody.richText.elems
val message = ArrayList<Message>(elements.size + 1) val message = MessageChainBuilder(elements.size + 1)
message.add(MessageSourceFromMsg(delegate = this)) message.add(MessageSourceFromMsg(delegate = this))
elements.joinToMessageChain(message) elements.joinToMessageChain(message)
return message.asMessageChain() return message.asMessageChain()
@ -338,7 +338,7 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain {
internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
val elements = this.elems!! val elements = this.elems!!
val message = ArrayList<Message>(elements.size + 1) val message = MessageChainBuilder(elements.size + 1)
message.add(MessageSourceFromServer(delegate = this)) message.add(MessageSourceFromServer(delegate = this))
elements.joinToMessageChain(message) elements.joinToMessageChain(message)
return message.asMessageChain() return message.asMessageChain()
@ -346,7 +346,7 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain {
@UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class) @UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class)
internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MutableList<Message>) { internal fun List<ImMsgBody.Elem>.joinToMessageChain(message: MessageChainBuilder) {
this.forEach { this.forEach {
when { when {
it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg))) it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg)))

View File

@ -285,7 +285,7 @@ internal class MessageSvc {
@UseExperimental(MiraiExperimentalAPI::class) @UseExperimental(MiraiExperimentalAPI::class)
fun startWaitingSequenceId(contact: Contact) { fun startWaitingSequenceId(contact: Contact) {
sequenceIdDeferred = contact.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int> { sequenceIdDeferred = contact.subscribingGetAsync<OnlinePush.PbPushGroupMsg.SendGroupMessageReceipt, Int>(timeoutMillis = 3000) {
if (it.messageRandom == this@MessageSourceFromSend.messageRandom) { if (it.messageRandom == this@MessageSourceFromSend.messageRandom) {
it.sequenceId it.sequenceId
} else null } else null

View File

@ -138,9 +138,5 @@ suspend inline fun <C : Contact> C.sendMessage(message: Message): MessageReceipt
/** /**
* @see Contact.sendMessage * @see Contact.sendMessage
*/ */
suspend inline fun <C : Contact> C.sendMessage(plain: String): MessageReceipt<C> = sendMessage(plain.toMessage()) @Suppress("UNCHECKED_CAST")
suspend inline fun <C : Contact> C.sendMessage(plain: String): MessageReceipt<C> = sendMessage(plain.toMessage())
/**
* @see Contact.sendMessage
*/
suspend inline fun <C : Contact> C.sendMessage(plain: CombinedMessage): MessageReceipt<C> = sendMessage(MessageChain(plain as Message)) as MessageReceipt<C>

View File

@ -26,7 +26,8 @@ import kotlin.jvm.JvmName
* *
* @see AtAll 全体成员 * @see AtAll 全体成员
*/ */
class At @MiraiInternalAPI constructor(val target: Long, val display: String) : Message { class At
@MiraiInternalAPI constructor(val target: Long, val display: String) : Message, MessageContent {
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
constructor(member: Member) : this(member.id, "@${member.nameCardOrNick}") constructor(member: Member) : this(member.id, "@${member.nameCardOrNick}")
@ -38,9 +39,9 @@ class At @MiraiInternalAPI constructor(val target: Long, val display: String) :
override fun followedBy(tail: Message): CombinedMessage { override fun followedBy(tail: Message): CombinedMessage {
if (tail is PlainText && tail.stringValue.startsWith(' ')) { if (tail is PlainText && tail.stringValue.startsWith(' ')) {
return super.followedBy(tail) return super<MessageContent>.followedBy(tail)
} }
return super.followedBy(PlainText(" ")) + tail return super<MessageContent>.followedBy(PlainText(" ")) + tail
} }
} }

View File

@ -22,15 +22,15 @@ import kotlin.jvm.JvmName
* *
* @see At at 单个群成员 * @see At at 单个群成员
*/ */
object AtAll : Message, Message.Key<AtAll> { object AtAll : Message, Message.Key<AtAll>, MessageContent {
override fun toString(): String = "@全体成员" override fun toString(): String = "@全体成员"
// 自动为消息补充 " " // 自动为消息补充 " "
override fun followedBy(tail: Message): CombinedMessage { override fun followedBy(tail: Message): CombinedMessage {
if (tail is PlainText && tail.stringValue.startsWith(' ')) { if (tail is PlainText && tail.stringValue.startsWith(' ')) {
return super.followedBy(tail) return super<MessageContent>.followedBy(tail)
} }
return super.followedBy(PlainText(" ")) + tail return super<MessageContent>.followedBy(PlainText(" ")) + tail
} }
} }

View File

@ -7,8 +7,14 @@
* https://github.com/mamoe/mirai/blob/master/LICENSE * https://github.com/mamoe/mirai/blob/master/LICENSE
*/ */
@file:JvmMultifileClass
@file:JvmName("MessageUtils")
package net.mamoe.mirai.message.data package net.mamoe.mirai.message.data
import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName
/** /**
* Left-biased list * Left-biased list
*/ */
@ -16,6 +22,8 @@ class CombinedMessage(
val left: Message, val left: Message,
val element: Message val element: Message
) : Iterable<Message>, Message { ) : Iterable<Message>, Message {
// 不要把它用作 local function, 会编译错误
private suspend fun SequenceScope<Message>.yieldCombinedOrElements(message: Message) { private suspend fun SequenceScope<Message>.yieldCombinedOrElements(message: Message) {
when (message) { when (message) {
is CombinedMessage -> { is CombinedMessage -> {
@ -36,6 +44,6 @@ class CombinedMessage(
} }
override fun toString(): String { override fun toString(): String {
return left.toString() + element.toString() return element.toString() + left.toString()
} }
} }

View File

@ -18,7 +18,7 @@ import kotlin.jvm.JvmName
/** /**
* QQ 自带表情 * QQ 自带表情
*/ */
class Face(val id: Int) : Message { class Face(val id: Int) : Message, MessageContent {
override fun toString(): String = "[mirai:face$id]" override fun toString(): String = "[mirai:face$id]"
/** /**

View File

@ -25,7 +25,7 @@ import kotlin.jvm.JvmStatic
/** /**
* 自定义表情 (收藏的表情), 图片 * 自定义表情 (收藏的表情), 图片
*/ */
sealed class Image : Message { sealed class Image : Message, MessageContent {
companion object Key : Message.Key<Image> { companion object Key : Message.Key<Image> {
@JvmStatic @JvmStatic
@JsName("fromId") @JsName("fromId")

View File

@ -20,6 +20,12 @@ import kotlin.jvm.JvmSynthetic
* 可发送的或从服务器接收的消息. * 可发送的或从服务器接收的消息.
* 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] . * 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] .
* *
* [消息][Message] 分为
* - [MessageMetadata] 消息元数据, 包括: [消息来源][MessageSource]
* - [MessageContent] 单个消息, 包括: [纯文本][PlainText], [@群员][At], [@全体成员][AtAll] .
* - [CombinedMessage] 通过 [plus] 连接的两个消息. 可通过 [asMessageChain] 转换为 [MessageChain]
* - [MessageChain] 不可变消息链, [List] 形式链接的多个 [Message] 实例.
*
* ** Kotlin 使用 [Message]** * ** Kotlin 使用 [Message]**
* 这与使用 [String] 的使用非常类似. * 这与使用 [String] 的使用非常类似.
* *
@ -44,7 +50,12 @@ import kotlin.jvm.JvmSynthetic
* @see PlainText 纯文本 * @see PlainText 纯文本
* @see Image 图片 * @see Image 图片
* @see Face 表情 * @see Face 表情
* @see At 引用一个群成员
* @see AtAll 引用全体成员
* @see QuoteReply 引用一条消息
*
* @see MessageChain 消息链( `List<Message>`) * @see MessageChain 消息链( `List<Message>`)
* @see buildMessageChain 构造一个 [MessageChain]
* *
* @see Contact.sendMessage 发送消息 * @see Contact.sendMessage 发送消息
*/ */
@ -86,8 +97,6 @@ interface Message {
*/ */
@JvmSynthetic // in java they should use `plus` instead @JvmSynthetic // in java they should use `plus` instead
fun followedBy(tail: Message): CombinedMessage { fun followedBy(tail: Message): CombinedMessage {
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
require(this !is SingleOnly) { "SingleOnly Message cannot be followed" }
return CombinedMessage(tail, this) return CombinedMessage(tail, this)
} }
@ -100,12 +109,37 @@ interface Message {
operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage()) operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage())
} }
suspend fun <C : Contact> Message.sendTo(contact: C): MessageReceipt<C> {
return contact.sendMessage(this)
}
interface SingleMessage : Message
/** /**
* 表示这个 [Message] 仅能单个存在, 无法被连接. * 消息元数据, 即不含内容的元素.
* 包括: [MessageSource]
*/ */
interface SingleOnly : Message interface MessageMetadata : SingleMessage {
/*
fun iterator(): Iterator<Message> {
return object : Iterator<Message> {
var visited: Boolean = false
override fun hasNext(): Boolean = !visited
override fun next(): Message {
if (visited) throw NoSuchElementException()
return this@MessageMetadata.also { visited = true }
}
}
}*/
}
/**
* 消息内容
*/
interface MessageContent : SingleMessage
/** /**
* [this] 发送给指定联系人 * [this] 发送给指定联系人
*/ */
suspend inline fun <C : Contact> Message.sendTo(contact: C): MessageReceipt<C> = contact.sendMessage(this) @Suppress("UNCHECKED_CAST")
suspend inline fun <C : Contact> MessageChain.sendTo(contact: C): MessageReceipt<C> = contact.sendMessage(this) as MessageReceipt<C>

View File

@ -16,27 +16,19 @@ import net.mamoe.mirai.message.data.NullMessageChain.toString
import kotlin.js.JsName import kotlin.js.JsName
import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmMultifileClass
import kotlin.jvm.JvmName import kotlin.jvm.JvmName
import kotlin.jvm.JvmSynthetic
import kotlin.reflect.KProperty import kotlin.reflect.KProperty
/** /**
* 消息链. MutableList<Message>. * 消息链.
* 它的一般实现为 [MessageChainImpl], `null` 实现为 [NullMessageChain] * 它的一般实现为 [MessageChainImpl], `null` 实现为 [NullMessageChain]
* *
* 有关 [MessageChain] 的创建和连接:
* - 当任意两个不是 [MessageChain] [Message] 相连接后, 将会产生一个 [MessageChain].
* - 若两个 [MessageChain] 连接, 后一个将会被合并到第一个内.
* - 若一个 [MessageChain] 与一个其他 [Message] 连接, [Message] 将会被添加入 [MessageChain].
* - 若一个 [Message] 与一个 [MessageChain] 连接, [Message] 将会被添加入 [MessageChain].
*
* 要获取更多信息, 请查看 [Message] * 要获取更多信息, 请查看 [Message]
* *
* @see buildMessageChain * @see buildMessageChain 构造一个 [MessageChain]
*/ */
interface MessageChain : Message, List<Message> { interface MessageChain : Message, Iterable<SingleMessage> {
// region Message override
override operator fun contains(sub: String): Boolean override operator fun contains(sub: String): Boolean
// endregion
override fun toString(): String override fun toString(): String
/** /**
@ -51,29 +43,16 @@ interface MessageChain : Message, List<Message> {
* 遍历每一个有内容的消息, [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage] * 遍历每一个有内容的消息, [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]
*/ */
inline fun MessageChain.foreachContent(block: (Message) -> Unit) { inline fun MessageChain.foreachContent(block: (Message) -> Unit) {
this.forEachIndexed { index: Int, message: Message -> var last: Message? = null
this.forEach { message: Message ->
if (message is At) { if (message is At) {
if (index == 0 || this[index - 1] !is QuoteReply) { if (last != null || last !is QuoteReply) {
block(message) block(message)
} }
} else if (message.hasContent()) { } else if (message is MessageContent) {
block(message) block(message)
} }
} last = message
}
/**
* 判断这个 [Message] 是否含有内容, 即是否为 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage]
*/
fun Message.hasContent(): Boolean {
return when (this) {
is At,
is AtAll,
is PlainText,
is Image,
is Face,
is XMLMessage -> true
else -> false
} }
} }
@ -99,7 +78,7 @@ fun MessageChain(): MessageChain = EmptyMessageChain
@Suppress("FunctionName") @Suppress("FunctionName")
fun MessageChain(vararg messages: Message): MessageChain = fun MessageChain(vararg messages: Message): MessageChain =
if (messages.isEmpty()) EmptyMessageChain if (messages.isEmpty()) EmptyMessageChain
else MessageChainImpl(messages.toMutableList()) else MessageChainImpl(messages.asSequence().flatten())
/** /**
* 构造 [MessageChain] 的快速途径 ( [Array] 创建) * 构造 [MessageChain] 的快速途径 ( [Array] 创建)
@ -109,7 +88,10 @@ fun MessageChain(vararg messages: Message): MessageChain =
@JsName("newChain") @JsName("newChain")
@Suppress("FunctionName") @Suppress("FunctionName")
fun MessageChain(message: Message): MessageChain = fun MessageChain(message: Message): MessageChain =
MessageChainImpl(listOf(message)) when (message) {
is SingleMessage -> SingleMessageChainImpl(message)
else -> MessageChainImpl(message.flatten().asIterable())
}
/** /**
* 构造 [MessageChain] * 构造 [MessageChain]
@ -118,7 +100,7 @@ fun MessageChain(message: Message): MessageChain =
@JsName("newChain") @JsName("newChain")
@Suppress("FunctionName") @Suppress("FunctionName")
fun MessageChain(messages: Iterable<Message>): MessageChain = fun MessageChain(messages: Iterable<Message>): MessageChain =
MessageChainImpl(messages.toList()) MessageChainImpl(messages.flatten().asIterable())
/** /**
* 构造 [MessageChain] * 构造 [MessageChain]
@ -127,7 +109,7 @@ fun MessageChain(messages: Iterable<Message>): MessageChain =
@JsName("newChain") @JsName("newChain")
@Suppress("FunctionName") @Suppress("FunctionName")
fun MessageChain(messages: List<Message>): MessageChain = fun MessageChain(messages: List<Message>): MessageChain =
MessageChainImpl(messages) MessageChainImpl(messages.flatten().asIterable())
/** /**
@ -135,14 +117,34 @@ fun MessageChain(messages: List<Message>): MessageChain =
* [this] [MessageChain] 将直接返回 this * [this] [MessageChain] 将直接返回 this
* 否则将调用 [MessageChain] 构造一个 [MessageChainImpl] * 否则将调用 [MessageChain] 构造一个 [MessageChainImpl]
*/ */
@Suppress("NOTHING_TO_INLINE") @Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST")
inline fun Message.toChain(): MessageChain = if (this is MessageChain) this else MessageChain(this) @JvmSynthetic
fun Message.toChain(): MessageChain = when (this) {
is MessageChain -> this
is CombinedMessage -> MessageChainImpl((this as Iterable<Message>).flatten().asIterable())
else -> SingleMessageChainImpl(this as SingleMessage)
}
/** @JvmName("asMessageChain1")
* 构造 [MessageChain] @JvmSynthetic
*/
@Suppress("unused", "NOTHING_TO_INLINE") @Suppress("unused", "NOTHING_TO_INLINE")
inline fun List<Message>.asMessageChain(): MessageChain = MessageChain(this) fun Iterable<SingleMessage>.asMessageChain(): MessageChain = MessageChainImpl(this)
@JvmSynthetic
@Suppress("unused", "NOTHING_TO_INLINE")
fun Iterable<Message>.asMessageChain(): MessageChain = MessageChainImpl(this.flatten())
fun Iterable<Message>.flatten(): Sequence<SingleMessage> = asSequence().flatten()
fun Sequence<Message>.flatten(): Sequence<SingleMessage> = flatMap { it.flatten() }
fun Message.flatten(): Sequence<SingleMessage> {
return when (this) {
is MessageChain -> this.asSequence()
is CombinedMessage -> this.asSequence().flatten()
else -> sequenceOf(this as SingleMessage)
}
}
/** /**
* 获取第一个 [M] 类型的 [Message] 实例 * 获取第一个 [M] 类型的 [Message] 实例
@ -189,9 +191,7 @@ fun <M : Message> MessageChain.first(key: Message.Key<M>): M = firstOrNull(key)
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun <M : Message> MessageChain.any(key: Message.Key<M>): Boolean = firstOrNull(key) != null fun <M : Message> MessageChain.any(key: Message.Key<M>): Boolean = firstOrNull(key) != null
object EmptyMessageChain : MessageChain by { object EmptyMessageChain : MessageChain by MessageChainImpl(emptyList())
MessageChainImpl(emptyList())
}()
/** /**
* Null [MessageChain]. * Null [MessageChain].
@ -200,45 +200,27 @@ object EmptyMessageChain : MessageChain by {
* [toString] , 其他方法均 [error] * [toString] , 其他方法均 [error]
*/ */
object NullMessageChain : MessageChain { object NullMessageChain : MessageChain {
override fun subList(fromIndex: Int, toIndex: Int): MutableList<Message> = error("accessing NullMessageChain")
override fun toString(): String = "null" override fun toString(): String = "null"
override fun contains(sub: String): Boolean = error("accessing NullMessageChain") override fun contains(sub: String): Boolean = error("accessing NullMessageChain")
override fun contains(element: Message): Boolean = error("accessing NullMessageChain")
override fun followedBy(tail: Message): CombinedMessage = CombinedMessage(left = EmptyMessageChain, element = tail) override fun followedBy(tail: Message): CombinedMessage = CombinedMessage(left = EmptyMessageChain, element = tail)
override fun iterator(): MutableIterator<SingleMessage> = error("accessing NullMessageChain")
override val size: Int get() = error("accessing NullMessageChain")
override fun containsAll(elements: Collection<Message>): Boolean = error("accessing NullMessageChain")
override fun get(index: Int): Message = error("accessing NullMessageChain")
override fun indexOf(element: Message): Int = error("accessing NullMessageChain")
override fun isEmpty(): Boolean = error("accessing NullMessageChain")
override fun iterator(): MutableIterator<Message> = error("accessing NullMessageChain")
override fun lastIndexOf(element: Message): Int = error("accessing NullMessageChain")
override fun listIterator(): MutableListIterator<Message> = error("accessing NullMessageChain")
override fun listIterator(index: Int): MutableListIterator<Message> = error("accessing NullMessageChain")
} }
/** @PublishedApi
* [MessageChain] 实现
* 它是一个特殊的 [Message], 实现 [MutableList] 接口, 但将所有的接口调用都转到内部维护的另一个 [MutableList].
*/
internal class MessageChainImpl constructor( internal class MessageChainImpl constructor(
/** private val delegate: Iterable<SingleMessage>
* Elements will not be instances of [MessageChain] ) : Message, Iterable<SingleMessage> by delegate, MessageChain {
*/ constructor(delegate: Sequence<SingleMessage>) : this(delegate.asIterable())
private val delegate: List<Message>
) : Message, List<Message> by delegate, MessageChain {
override fun toString(): String = this.delegate.joinToString("") { it.toString() }
override fun toString(): String = this.delegate.joinToString("") { it.toString() }
override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) } override operator fun contains(sub: String): Boolean = delegate.any { it.contains(sub) }
override fun followedBy(tail: Message): CombinedMessage {
require(tail !is SingleOnly) { "SingleOnly Message cannot follow another message" }
// if (tail is MessageChain) tail.forEach { child -> this.followedBy(child) }
// else this.delegate.add(tail)
return CombinedMessage(tail, this)
}
} }
@PublishedApi
internal class SingleMessageChainImpl constructor(
private val delegate: SingleMessage
) : Message, Iterable<SingleMessage> by listOf(delegate), MessageChain {
override fun toString(): String = this.delegate.toString()
override operator fun contains(sub: String): Boolean = sub in delegate
}

View File

@ -23,7 +23,7 @@ import kotlin.jvm.JvmName
* *
* @see MessageSource.quote 引用这条消息, 创建 [MessageChain] * @see MessageSource.quote 引用这条消息, 创建 [MessageChain]
*/ */
interface MessageSource : Message { interface MessageSource : Message, MessageMetadata {
companion object Key : Message.Key<MessageSource> companion object Key : Message.Key<MessageSource>
/** /**

View File

@ -21,7 +21,7 @@ import kotlin.jvm.JvmStatic
* *
* 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [MessageChain.plus] * 一般不需要主动构造 [PlainText], [Message] 可直接与 [String] 相加. Java 用户请使用 [MessageChain.plus]
*/ */
inline class PlainText(val stringValue: String) : Message { inline class PlainText(val stringValue: String) : Message, MessageContent {
constructor(charSequence: CharSequence) : this(charSequence.toString()) constructor(charSequence: CharSequence) : this(charSequence.toString())
override operator fun contains(sub: String): Boolean = sub in stringValue override operator fun contains(sub: String): Boolean = sub in stringValue

View File

@ -21,25 +21,27 @@ import kotlin.jvm.JvmName
/** /**
* 群内的或好友的引用回复. * 从服务器接收的或客户端构造用来发送的群内的或好友的引用回复.
* *
* 可以引用一条群消息并发送给一个好友, 或是引用好友消息发送给群. * 可以引用一条群消息并发送给一个好友, 或是引用好友消息发送给群.
* 可以引用自己发出的消息. 详见 [MessageReceipt.quote] * 可以引用自己发出的消息. 详见 [MessageReceipt.quote]
* *
* 总是使用 [quote] 来构造这个实例. * 总是使用 [quote] 来构造这个实例.
*/ */
open class QuoteReply @MiraiInternalAPI constructor(val source: MessageSource) : Message { open class QuoteReply
@MiraiInternalAPI constructor(val source: MessageSource) : Message, MessageContent {
companion object Key : Message.Key<QuoteReply> companion object Key : Message.Key<QuoteReply>
override fun toString(): String = "" override fun toString(): String = ""
} }
/** /**
* 群内的引用回复. * 用于发送的引用回复.
* 总是使用 [quote] 来构造实例. * 总是使用 [quote] 来构造实例.
*/ */
@UseExperimental(MiraiInternalAPI::class) @UseExperimental(MiraiInternalAPI::class)
class QuoteReplyToSend @MiraiInternalAPI constructor(source: MessageSource, val sender: QQ) : QuoteReply(source) { class QuoteReplyToSend
@MiraiInternalAPI constructor(source: MessageSource, val sender: QQ) : QuoteReply(source) {
fun createAt(): At = At(sender as Member) fun createAt(): At = At(sender as Member)
} }

View File

@ -23,8 +23,8 @@ import kotlin.jvm.JvmName
* *
* @see buildXMLMessage * @see buildXMLMessage
*/ */
inline class XMLMessage(val stringValue: String) : Message, @MiraiExperimentalAPI
SingleOnly { inline class XMLMessage(val stringValue: String) : Message, MessageContent {
override fun followedBy(tail: Message): Nothing = error("XMLMessage Message cannot be followed") override fun followedBy(tail: Message): Nothing = error("XMLMessage Message cannot be followed")
override fun toString(): String = stringValue override fun toString(): String = stringValue
} }

View File

@ -22,8 +22,10 @@ inline fun buildMessageChain(block: MessageChainBuilder.() -> Unit): MessageChai
} }
class MessageChainBuilder @JvmOverloads constructor( class MessageChainBuilder @JvmOverloads constructor(
private val container: MutableList<Message> = mutableListOf() private val container: MutableCollection<Message> = mutableListOf()
) : MutableList<Message> by container, Appendable { ) : MutableCollection<Message> by container, Appendable {
constructor(initialSize: Int) : this(ArrayList<Message>(initialSize))
operator fun Message.unaryPlus() { operator fun Message.unaryPlus() {
add(this) add(this)
} }

View File

@ -56,6 +56,7 @@ internal class LockFreeLinkedListTest {
//} //}
} }
@Suppress("UNREACHABLE_CODE", "DeferredResultUnused")
@Test @Test
fun `so many concurrent add remove and foreach`() = runBlocking { fun `so many concurrent add remove and foreach`() = runBlocking {
return@runBlocking // 测试通过了, 加快速度. 因为 kotlin 一些其他 bug return@runBlocking // 测试通过了, 加快速度. 因为 kotlin 一些其他 bug