diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt index 187c49a0e..8e00bc629 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/MessageDTO.kt @@ -103,6 +103,7 @@ suspend fun MessagePacket<*, *>.toDTO() = when (this) { suspend fun MessageChainDTO.toMessageChain(contact: Contact) = buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } } + @UseExperimental(ExperimentalUnsignedTypes::class) suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) { is MessageSource -> MessageSourceDTO(message.id) diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt index 453c5adb2..e80676438 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/ConfigSection.kt @@ -17,8 +17,11 @@ import com.moandjiezana.toml.Toml import com.moandjiezana.toml.TomlWriter import kotlinx.serialization.Serializable import kotlinx.serialization.UnstableDefault +import net.mamoe.mirai.utils.io.encodeToString import org.yaml.snakeyaml.Yaml +import tornadofx.c import java.io.File +import java.io.InputStream import java.util.* import java.util.concurrent.ConcurrentHashMap import kotlin.collections.LinkedHashMap @@ -69,6 +72,9 @@ interface Config { ) } + /** + * create a read-write config + * */ fun load(file: File): Config { if (!file.exists()) { file.createNewFile() @@ -86,6 +92,32 @@ interface Config { else -> error("Unsupported file config type ${file.extension.toLowerCase()}") } } + + /** + * create a read-only config + */ + fun load(content: String, type: String): Config { + return when (type.toLowerCase()) { + "json" -> JsonConfig(content) + "yml" -> YamlConfig(content) + "yaml" -> YamlConfig(content) + "mirai" -> YamlConfig(content) + "ini" -> TomlConfig(content) + "toml" -> TomlConfig(content) + "properties" -> TomlConfig(content) + "property" -> TomlConfig(content) + "data" -> TomlConfig(content) + else -> error("Unsupported file config type $content") + } + } + + /** + * create a read-only config + */ + fun load(inputStream: InputStream, type: String): Config { + return load(inputStream.readBytes().encodeToString(), type) + } + } } @@ -363,14 +395,23 @@ interface FileConfig : Config { abstract class FileConfigImpl internal constructor( - private val file: File + private val rawContent: String ) : FileConfig, ConfigSection { - private val content by lazy { - deserialize(file.readText()) + internal var file: File? = null + + + constructor(file: File) : this(file.readText()) { + this.file = file } + + private val content by lazy { + deserialize(rawContent) + } + + override val size: Int get() = content.size override val entries: MutableSet> get() = content.entries override val keys: MutableSet get() = content.keys @@ -384,12 +425,17 @@ abstract class FileConfigImpl internal constructor( override fun remove(key: String): Any? = content.remove(key) override fun save() { - if (!file.exists()) { - file.createNewFile() + if (isReadOnly()) { + error("Config is readonly") } - file.writeText(serialize(content)) + if (!((file?.exists())!!)) { + file?.createNewFile() + } + file?.writeText(serialize(content)) } + fun isReadOnly() = file == null + override fun contains(key: String): Boolean { return content.contains(key) } @@ -409,8 +455,12 @@ abstract class FileConfigImpl internal constructor( } class JsonConfig internal constructor( - file: File -) : FileConfigImpl(file) { + content: String +) : FileConfigImpl(content) { + constructor(file: File) : this(file.readText()) { + this.file = file + } + @UnstableDefault override fun deserialize(content: String): ConfigSection { if (content.isEmpty() || content.isBlank() || content == "{}") { @@ -429,7 +479,11 @@ class JsonConfig internal constructor( } } -class YamlConfig internal constructor(file: File) : FileConfigImpl(file) { +class YamlConfig internal constructor(content: String) : FileConfigImpl(content) { + constructor(file: File) : this(file.readText()) { + this.file = file + } + override fun deserialize(content: String): ConfigSection { if (content.isEmpty() || content.isBlank()) { return ConfigSectionImpl() @@ -447,7 +501,11 @@ class YamlConfig internal constructor(file: File) : FileConfigImpl(file) { } -class TomlConfig internal constructor(file: File) : FileConfigImpl(file) { +class TomlConfig internal constructor(content: String) : FileConfigImpl(content) { + constructor(file: File) : this(file.readText()) { + this.file = file + } + override fun deserialize(content: String): ConfigSection { if (content.isEmpty() || content.isBlank()) { return ConfigSectionImpl() diff --git a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt index dd526deee..4d1bd6f07 100644 --- a/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt +++ b/mirai-console/src/main/kotlin/net/mamoe/mirai/console/plugins/PluginBase.kt @@ -17,6 +17,7 @@ import net.mamoe.mirai.utils.MiraiLogger import net.mamoe.mirai.utils.SimpleLogger import net.mamoe.mirai.utils.io.encodeToString import java.io.File +import java.io.InputStream import java.net.URL import java.net.URLClassLoader import java.util.jar.JarFile @@ -91,6 +92,13 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope { val logger: MiraiLogger by lazy { DefaultLogger(pluginDescription.name) } + + fun getResources(fileName: String): InputStream? { + return PluginManager.getFileInJarByName( + this.pluginDescription.name, + fileName + ) + } } class PluginDescription( @@ -325,6 +333,47 @@ object PluginManager { it.disable(throwable) } } + + /** + * 根据插件名字找Jar的文件 + * null => 没找到 + */ + fun getJarPath(pluginName: String): File? { + File(pluginsPath).listFiles()?.forEach { file -> + if (file != null && file.extension == "jar") { + val jar = JarFile(file) + val pluginYml = + jar.entries().asSequence().filter { it.name.toLowerCase().contains("plugin.yml") }.firstOrNull() + if (pluginYml != null) { + val description = + PluginDescription.readFromContent( + URL("jar:file:" + file.absoluteFile + "!/" + pluginYml.name).openConnection().inputStream.use { + it.readBytes().encodeToString() + }) + if (description.name.toLowerCase() == pluginName.toLowerCase()) { + return file + } + } + } + } + return null + } + + + /** + * 根据插件名字找Jar resources中的文件 + * null => 没找到 + */ + fun getFileInJarByName(pluginName: String, toFind: String): InputStream? { + val jarFile = getJarPath(pluginName) + if (jarFile == null) { + return null + } + val jar = JarFile(jarFile) + val toFindFile = + jar.entries().asSequence().filter { it.name == toFind }.firstOrNull() ?: return null + return URL("jar:file:" + jarFile.absoluteFile + "!/" + toFindFile.name).openConnection().inputStream + } } diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt index 86261c518..875502082 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/message/messages.kt @@ -326,7 +326,7 @@ internal class NotOnlineImageFromServer( internal fun MsgComm.Msg.toMessageChain(): MessageChain { val elements = this.msgBody.richText.elems - val message = ArrayList(elements.size + 1) + val message = MessageChainBuilder(elements.size + 1) message.add(MessageSourceFromMsg(delegate = this)) elements.joinToMessageChain(message) return message.asMessageChain() @@ -338,7 +338,7 @@ internal fun MsgComm.Msg.toMessageChain(): MessageChain { internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { val elements = this.elems!! - val message = ArrayList(elements.size + 1) + val message = MessageChainBuilder(elements.size + 1) message.add(MessageSourceFromServer(delegate = this)) elements.joinToMessageChain(message) return message.asMessageChain() @@ -346,7 +346,7 @@ internal fun ImMsgBody.SourceMsg.toMessageChain(): MessageChain { @UseExperimental(MiraiInternalAPI::class, ExperimentalUnsignedTypes::class, MiraiDebugAPI::class) -internal fun List.joinToMessageChain(message: MutableList) { +internal fun List.joinToMessageChain(message: MessageChainBuilder) { this.forEach { when { it.srcMsg != null -> message.add(QuoteReply(MessageSourceFromServer(it.srcMsg))) diff --git a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt index b2dab4e51..7207413db 100644 --- a/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt +++ b/mirai-core-qqandroid/src/commonMain/kotlin/net/mamoe/mirai/qqandroid/network/protocol/packet/chat/receive/MessageSvc.kt @@ -285,7 +285,7 @@ internal class MessageSvc { @UseExperimental(MiraiExperimentalAPI::class) fun startWaitingSequenceId(contact: Contact) { - sequenceIdDeferred = contact.subscribingGetAsync { + sequenceIdDeferred = contact.subscribingGetAsync(timeoutMillis = 3000) { if (it.messageRandom == this@MessageSourceFromSend.messageRandom) { it.sequenceId } else null diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt index 07cf36239..678af1d00 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/contact/Contact.kt @@ -138,9 +138,5 @@ suspend inline fun C.sendMessage(message: Message): MessageReceipt /** * @see Contact.sendMessage */ -suspend inline fun C.sendMessage(plain: String): MessageReceipt = sendMessage(plain.toMessage()) - -/** - * @see Contact.sendMessage - */ -suspend inline fun C.sendMessage(plain: CombinedMessage): MessageReceipt = sendMessage(MessageChain(plain as Message)) as MessageReceipt \ No newline at end of file +@Suppress("UNCHECKED_CAST") +suspend inline fun C.sendMessage(plain: String): MessageReceipt = sendMessage(plain.toMessage()) \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt index e21f476ea..f2413eea3 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/At.kt @@ -26,7 +26,8 @@ import kotlin.jvm.JvmName * * @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) 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 { if (tail is PlainText && tail.stringValue.startsWith(' ')) { - return super.followedBy(tail) + return super.followedBy(tail) } - return super.followedBy(PlainText(" ")) + tail + return super.followedBy(PlainText(" ")) + tail } } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt index 0b9369bdd..df748ddd5 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/AtAll.kt @@ -22,15 +22,15 @@ import kotlin.jvm.JvmName * * @see At at 单个群成员 */ -object AtAll : Message, Message.Key { +object AtAll : Message, Message.Key, MessageContent { override fun toString(): String = "@全体成员" // 自动为消息补充 " " override fun followedBy(tail: Message): CombinedMessage { if (tail is PlainText && tail.stringValue.startsWith(' ')) { - return super.followedBy(tail) + return super.followedBy(tail) } - return super.followedBy(PlainText(" ")) + tail + return super.followedBy(PlainText(" ")) + tail } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt index 081c1116b..c29b85149 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/CombinedMessage.kt @@ -7,8 +7,14 @@ * https://github.com/mamoe/mirai/blob/master/LICENSE */ +@file:JvmMultifileClass +@file:JvmName("MessageUtils") + package net.mamoe.mirai.message.data +import kotlin.jvm.JvmMultifileClass +import kotlin.jvm.JvmName + /** * Left-biased list */ @@ -16,6 +22,8 @@ class CombinedMessage( val left: Message, val element: Message ) : Iterable, Message { + + // 不要把它用作 local function, 会编译错误 private suspend fun SequenceScope.yieldCombinedOrElements(message: Message) { when (message) { is CombinedMessage -> { @@ -36,6 +44,6 @@ class CombinedMessage( } override fun toString(): String { - return left.toString() + element.toString() + return element.toString() + left.toString() } } \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt index 0578b7c9d..88da0932c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Face.kt @@ -18,7 +18,7 @@ import kotlin.jvm.JvmName /** * QQ 自带表情 */ -class Face(val id: Int) : Message { +class Face(val id: Int) : Message, MessageContent { override fun toString(): String = "[mirai:face$id]" /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt index d11c80488..f86ff59b4 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Image.kt @@ -25,7 +25,7 @@ import kotlin.jvm.JvmStatic /** * 自定义表情 (收藏的表情), 图片 */ -sealed class Image : Message { +sealed class Image : Message, MessageContent { companion object Key : Message.Key { @JvmStatic @JsName("fromId") diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt index aba05812d..95e068f0a 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/Message.kt @@ -20,6 +20,12 @@ import kotlin.jvm.JvmSynthetic * 可发送的或从服务器接收的消息. * 采用这样的消息模式是因为 QQ 的消息多元化, 一条消息中可包含 [纯文本][PlainText], [图片][Image] 等. * + * [消息][Message] 分为 + * - [MessageMetadata] 消息元数据, 包括: [消息来源][MessageSource] + * - [MessageContent] 单个消息, 包括: [纯文本][PlainText], [@群员][At], [@全体成员][AtAll] 等. + * - [CombinedMessage] 通过 [plus] 连接的两个消息. 可通过 [asMessageChain] 转换为 [MessageChain] + * - [MessageChain] 不可变消息链, 即 [List] 形式链接的多个 [Message] 实例. + * * **在 Kotlin 使用 [Message]** * 这与使用 [String] 的使用非常类似. * @@ -44,7 +50,12 @@ import kotlin.jvm.JvmSynthetic * @see PlainText 纯文本 * @see Image 图片 * @see Face 表情 + * @see At 引用一个群成员 + * @see AtAll 引用全体成员 + * @see QuoteReply 引用一条消息 + * * @see MessageChain 消息链(即 `List`) + * @see buildMessageChain 构造一个 [MessageChain] * * @see Contact.sendMessage 发送消息 */ @@ -86,8 +97,6 @@ interface Message { */ @JvmSynthetic // in java they should use `plus` instead 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) } @@ -100,12 +109,37 @@ interface Message { operator fun plus(another: CharSequence): CombinedMessage = this.followedBy(another.toString().toMessage()) } +suspend fun Message.sendTo(contact: C): MessageReceipt { + return contact.sendMessage(this) +} + +interface SingleMessage : Message + /** - * 表示这个 [Message] 仅能单个存在, 无法被连接. + * 消息元数据, 即不含内容的元素. + * 包括: [MessageSource] */ -interface SingleOnly : Message +interface MessageMetadata : SingleMessage { + /* + fun iterator(): Iterator { + return object : Iterator { + 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] 发送给指定联系人 */ -suspend inline fun Message.sendTo(contact: C): MessageReceipt = contact.sendMessage(this) \ No newline at end of file +@Suppress("UNCHECKED_CAST") +suspend inline fun MessageChain.sendTo(contact: C): MessageReceipt = contact.sendMessage(this) as MessageReceipt \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt index 529121372..54022e572 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageChain.kt @@ -16,27 +16,19 @@ import net.mamoe.mirai.message.data.NullMessageChain.toString import kotlin.js.JsName import kotlin.jvm.JvmMultifileClass import kotlin.jvm.JvmName +import kotlin.jvm.JvmSynthetic import kotlin.reflect.KProperty /** - * 消息链. 即 MutableList. + * 消息链. * 它的一般实现为 [MessageChainImpl], `null` 实现为 [NullMessageChain] * - * 有关 [MessageChain] 的创建和连接: - * - 当任意两个不是 [MessageChain] 的 [Message] 相连接后, 将会产生一个 [MessageChain]. - * - 若两个 [MessageChain] 连接, 后一个将会被合并到第一个内. - * - 若一个 [MessageChain] 与一个其他 [Message] 连接, [Message] 将会被添加入 [MessageChain]. - * - 若一个 [Message] 与一个 [MessageChain] 连接, [Message] 将会被添加入 [MessageChain]. - * * 要获取更多信息, 请查看 [Message] * - * @see buildMessageChain + * @see buildMessageChain 构造一个 [MessageChain] */ -interface MessageChain : Message, List { - // region Message override +interface MessageChain : Message, Iterable { override operator fun contains(sub: String): Boolean - // endregion - override fun toString(): String /** @@ -51,29 +43,16 @@ interface MessageChain : Message, List { * 遍历每一个有内容的消息, 即 [At], [AtAll], [PlainText], [Image], [Face], [XMLMessage] */ 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 (index == 0 || this[index - 1] !is QuoteReply) { + if (last != null || last !is QuoteReply) { block(message) } - } else if (message.hasContent()) { + } else if (message is MessageContent) { block(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 + last = message } } @@ -99,7 +78,7 @@ fun MessageChain(): MessageChain = EmptyMessageChain @Suppress("FunctionName") fun MessageChain(vararg messages: Message): MessageChain = if (messages.isEmpty()) EmptyMessageChain - else MessageChainImpl(messages.toMutableList()) + else MessageChainImpl(messages.asSequence().flatten()) /** * 构造 [MessageChain] 的快速途径 (无 [Array] 创建) @@ -109,7 +88,10 @@ fun MessageChain(vararg messages: Message): MessageChain = @JsName("newChain") @Suppress("FunctionName") fun MessageChain(message: Message): MessageChain = - MessageChainImpl(listOf(message)) + when (message) { + is SingleMessage -> SingleMessageChainImpl(message) + else -> MessageChainImpl(message.flatten().asIterable()) + } /** * 构造 [MessageChain] @@ -118,7 +100,7 @@ fun MessageChain(message: Message): MessageChain = @JsName("newChain") @Suppress("FunctionName") fun MessageChain(messages: Iterable): MessageChain = - MessageChainImpl(messages.toList()) + MessageChainImpl(messages.flatten().asIterable()) /** * 构造 [MessageChain] @@ -127,7 +109,7 @@ fun MessageChain(messages: Iterable): MessageChain = @JsName("newChain") @Suppress("FunctionName") fun MessageChain(messages: List): MessageChain = - MessageChainImpl(messages) + MessageChainImpl(messages.flatten().asIterable()) /** @@ -135,14 +117,34 @@ fun MessageChain(messages: List): MessageChain = * 若 [this] 为 [MessageChain] 将直接返回 this * 否则将调用 [MessageChain] 构造一个 [MessageChainImpl] */ -@Suppress("NOTHING_TO_INLINE") -inline fun Message.toChain(): MessageChain = if (this is MessageChain) this else MessageChain(this) +@Suppress("NOTHING_TO_INLINE", "UNCHECKED_CAST") +@JvmSynthetic +fun Message.toChain(): MessageChain = when (this) { + is MessageChain -> this + is CombinedMessage -> MessageChainImpl((this as Iterable).flatten().asIterable()) + else -> SingleMessageChainImpl(this as SingleMessage) +} -/** - * 构造 [MessageChain] - */ +@JvmName("asMessageChain1") +@JvmSynthetic @Suppress("unused", "NOTHING_TO_INLINE") -inline fun List.asMessageChain(): MessageChain = MessageChain(this) +fun Iterable.asMessageChain(): MessageChain = MessageChainImpl(this) + +@JvmSynthetic +@Suppress("unused", "NOTHING_TO_INLINE") +fun Iterable.asMessageChain(): MessageChain = MessageChainImpl(this.flatten()) + +fun Iterable.flatten(): Sequence = asSequence().flatten() + +fun Sequence.flatten(): Sequence = flatMap { it.flatten() } + +fun Message.flatten(): Sequence { + return when (this) { + is MessageChain -> this.asSequence() + is CombinedMessage -> this.asSequence().flatten() + else -> sequenceOf(this as SingleMessage) + } +} /** * 获取第一个 [M] 类型的 [Message] 实例 @@ -189,9 +191,7 @@ fun MessageChain.first(key: Message.Key): M = firstOrNull(key) @Suppress("UNCHECKED_CAST") fun MessageChain.any(key: Message.Key): Boolean = firstOrNull(key) != null -object EmptyMessageChain : MessageChain by { - MessageChainImpl(emptyList()) -}() +object EmptyMessageChain : MessageChain by MessageChainImpl(emptyList()) /** * Null 的 [MessageChain]. @@ -200,45 +200,27 @@ object EmptyMessageChain : MessageChain by { * 除 [toString] 外, 其他方法均 [error] */ object NullMessageChain : MessageChain { - override fun subList(fromIndex: Int, toIndex: Int): MutableList = error("accessing NullMessageChain") - override fun toString(): String = "null" - 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 val size: Int get() = error("accessing NullMessageChain") - override fun containsAll(elements: Collection): 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 = error("accessing NullMessageChain") - - override fun lastIndexOf(element: Message): Int = error("accessing NullMessageChain") - override fun listIterator(): MutableListIterator = error("accessing NullMessageChain") - - override fun listIterator(index: Int): MutableListIterator = error("accessing NullMessageChain") + override fun iterator(): MutableIterator = error("accessing NullMessageChain") } -/** - * [MessageChain] 实现 - * 它是一个特殊的 [Message], 实现 [MutableList] 接口, 但将所有的接口调用都转到内部维护的另一个 [MutableList]. - */ +@PublishedApi internal class MessageChainImpl constructor( - /** - * Elements will not be instances of [MessageChain] - */ - private val delegate: List -) : Message, List by delegate, MessageChain { - override fun toString(): String = this.delegate.joinToString("") { it.toString() } + private val delegate: Iterable +) : Message, Iterable by delegate, MessageChain { + constructor(delegate: Sequence) : this(delegate.asIterable()) + override fun toString(): String = this.delegate.joinToString("") { it.toString() } 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 by listOf(delegate), MessageChain { + override fun toString(): String = this.delegate.toString() + + override operator fun contains(sub: String): Boolean = sub in delegate +} \ No newline at end of file diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt index e57f8711c..992129835 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/MessageSource.kt @@ -23,7 +23,7 @@ import kotlin.jvm.JvmName * * @see MessageSource.quote 引用这条消息, 创建 [MessageChain] */ -interface MessageSource : Message { +interface MessageSource : Message, MessageMetadata { companion object Key : Message.Key /** diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt index 36fc92c02..e76a34e2b 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/PlainText.kt @@ -21,7 +21,7 @@ import kotlin.jvm.JvmStatic * * 一般不需要主动构造 [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()) override operator fun contains(sub: String): Boolean = sub in stringValue diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt index 494a1f280..137389190 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/QuoteReply.kt @@ -21,25 +21,27 @@ import kotlin.jvm.JvmName /** - * 群内的或好友的引用回复. + * 从服务器接收的或客户端构造用来发送的群内的或好友的引用回复. * * 可以引用一条群消息并发送给一个好友, 或是引用好友消息发送给群. * 可以引用自己发出的消息. 详见 [MessageReceipt.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 override fun toString(): String = "" } /** - * 群内的引用回复. + * 用于发送的引用回复. * 总是使用 [quote] 来构造实例. */ @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) } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt index 83bd973a5..fb01e7c6c 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/XML.kt @@ -23,8 +23,8 @@ import kotlin.jvm.JvmName * * @see buildXMLMessage */ -inline class XMLMessage(val stringValue: String) : Message, - SingleOnly { +@MiraiExperimentalAPI +inline class XMLMessage(val stringValue: String) : Message, MessageContent { override fun followedBy(tail: Message): Nothing = error("XMLMessage Message cannot be followed") override fun toString(): String = stringValue } diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/builder.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/builder.kt index d2d11b445..86060a517 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/builder.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/data/builder.kt @@ -22,8 +22,10 @@ inline fun buildMessageChain(block: MessageChainBuilder.() -> Unit): MessageChai } class MessageChainBuilder @JvmOverloads constructor( - private val container: MutableList = mutableListOf() -) : MutableList by container, Appendable { + private val container: MutableCollection = mutableListOf() +) : MutableCollection by container, Appendable { + constructor(initialSize: Int) : this(ArrayList(initialSize)) + operator fun Message.unaryPlus() { add(this) } diff --git a/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt b/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt index 7de786955..de4c3b195 100644 --- a/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt +++ b/mirai-core/src/jvmTest/kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt @@ -56,6 +56,7 @@ internal class LockFreeLinkedListTest { //} } + @Suppress("UNREACHABLE_CODE", "DeferredResultUnused") @Test fun `so many concurrent add remove and foreach`() = runBlocking { return@runBlocking // 测试通过了, 加快速度. 因为 kotlin 一些其他 bug diff --git a/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt b/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt index 9bf69d9a9..2e575f86d 100644 --- a/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt +++ b/mirai-plugins/image-sender/src/main/java/net/mamoe/mirai/imageplugin/ImageSenderMain.kt @@ -12,17 +12,17 @@ package net.mamoe.mirai.imageplugin import kotlinx.coroutines.* import net.mamoe.mirai.console.plugins.Config import net.mamoe.mirai.console.plugins.ConfigSection +import net.mamoe.mirai.console.plugins.PluginBase +import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.event.events.BotOnlineEvent import net.mamoe.mirai.event.subscribeAlways import net.mamoe.mirai.event.subscribeMessages -import net.mamoe.mirai.console.plugins.PluginBase -import net.mamoe.mirai.contact.Contact import net.mamoe.mirai.message.data.Image import net.mamoe.mirai.message.uploadAsImage import net.mamoe.mirai.utils.MiraiExperimentalAPI import org.jsoup.Jsoup import java.io.File -import kotlin.random.Random +import java.net.URL class ImageSenderMain : PluginBase() { @@ -60,7 +60,6 @@ class ImageSenderMain : PluginBase() { reply(e.message ?: "unknown error") } } - } } } @@ -84,16 +83,21 @@ class ImageSenderMain : PluginBase() { override fun onLoad() { logger.info("loading local image data") + try { - images = Config.load(this.javaClass.classLoader.getResource("data.yml")!!.path!!) + images = Config.load(getResources(fileName = "data.yml")!!, "yml") } catch (e: Exception) { + e.printStackTrace() logger.info("无法加载本地图片") } logger.info("本地图片版本" + images.getString("version")) - logger.info("Normal * " + images.getList("normal").size) - logger.info("R18 * " + images.getList("R18").size) + r18 = images.getConfigSectionList("R18") + normal = images.getConfigSectionList("normal") + logger.info("Normal * " + normal.size) + logger.info("R18 * " + r18.size) } + override fun onDisable() { }