Http api fix conflict

This commit is contained in:
ryoii 2020-02-23 22:53:56 +08:00
commit 1660e5b938
20 changed files with 264 additions and 126 deletions

View File

@ -103,6 +103,7 @@ suspend fun MessagePacket<*, *>.toDTO() = when (this) {
suspend fun MessageChainDTO.toMessageChain(contact: Contact) = suspend fun MessageChainDTO.toMessageChain(contact: Contact) =
buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } } buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } }
@UseExperimental(ExperimentalUnsignedTypes::class) @UseExperimental(ExperimentalUnsignedTypes::class)
suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) { suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) {
is MessageSource -> MessageSourceDTO(message.id) is MessageSource -> MessageSourceDTO(message.id)

View File

@ -17,8 +17,11 @@ import com.moandjiezana.toml.Toml
import com.moandjiezana.toml.TomlWriter import com.moandjiezana.toml.TomlWriter
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.UnstableDefault import kotlinx.serialization.UnstableDefault
import net.mamoe.mirai.utils.io.encodeToString
import org.yaml.snakeyaml.Yaml import org.yaml.snakeyaml.Yaml
import tornadofx.c
import java.io.File import java.io.File
import java.io.InputStream
import java.util.* import java.util.*
import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.LinkedHashMap import kotlin.collections.LinkedHashMap
@ -69,6 +72,9 @@ interface Config {
) )
} }
/**
* create a read-write config
* */
fun load(file: File): Config { fun load(file: File): Config {
if (!file.exists()) { if (!file.exists()) {
file.createNewFile() file.createNewFile()
@ -86,6 +92,32 @@ interface Config {
else -> error("Unsupported file config type ${file.extension.toLowerCase()}") 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( abstract class FileConfigImpl internal constructor(
private val file: File private val rawContent: String
) : FileConfig, ) : FileConfig,
ConfigSection { ConfigSection {
private val content by lazy { internal var file: File? = null
deserialize(file.readText())
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 size: Int get() = content.size
override val entries: MutableSet<MutableMap.MutableEntry<String, Any>> get() = content.entries override val entries: MutableSet<MutableMap.MutableEntry<String, Any>> get() = content.entries
override val keys: MutableSet<String> get() = content.keys override val keys: MutableSet<String> get() = content.keys
@ -384,12 +425,17 @@ abstract class FileConfigImpl internal constructor(
override fun remove(key: String): Any? = content.remove(key) override fun remove(key: String): Any? = content.remove(key)
override fun save() { override fun save() {
if (!file.exists()) { if (isReadOnly()) {
file.createNewFile() 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 { override fun contains(key: String): Boolean {
return content.contains(key) return content.contains(key)
} }
@ -409,8 +455,12 @@ abstract class FileConfigImpl internal constructor(
} }
class JsonConfig internal constructor( class JsonConfig internal constructor(
file: File content: String
) : FileConfigImpl(file) { ) : FileConfigImpl(content) {
constructor(file: File) : this(file.readText()) {
this.file = file
}
@UnstableDefault @UnstableDefault
override fun deserialize(content: String): ConfigSection { override fun deserialize(content: String): ConfigSection {
if (content.isEmpty() || content.isBlank() || content == "{}") { 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 { override fun deserialize(content: String): ConfigSection {
if (content.isEmpty() || content.isBlank()) { if (content.isEmpty() || content.isBlank()) {
return ConfigSectionImpl() 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 { override fun deserialize(content: String): ConfigSection {
if (content.isEmpty() || content.isBlank()) { if (content.isEmpty() || content.isBlank()) {
return ConfigSectionImpl() return ConfigSectionImpl()

View File

@ -17,6 +17,7 @@ import net.mamoe.mirai.utils.MiraiLogger
import net.mamoe.mirai.utils.SimpleLogger import net.mamoe.mirai.utils.SimpleLogger
import net.mamoe.mirai.utils.io.encodeToString import net.mamoe.mirai.utils.io.encodeToString
import java.io.File import java.io.File
import java.io.InputStream
import java.net.URL import java.net.URL
import java.net.URLClassLoader import java.net.URLClassLoader
import java.util.jar.JarFile import java.util.jar.JarFile
@ -91,6 +92,13 @@ abstract class PluginBase(coroutineContext: CoroutineContext) : CoroutineScope {
val logger: MiraiLogger by lazy { val logger: MiraiLogger by lazy {
DefaultLogger(pluginDescription.name) DefaultLogger(pluginDescription.name)
} }
fun getResources(fileName: String): InputStream? {
return PluginManager.getFileInJarByName(
this.pluginDescription.name,
fileName
)
}
} }
class PluginDescription( class PluginDescription(
@ -325,6 +333,47 @@ object PluginManager {
it.disable(throwable) 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
}
} }

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

View File

@ -12,17 +12,17 @@ package net.mamoe.mirai.imageplugin
import kotlinx.coroutines.* import kotlinx.coroutines.*
import net.mamoe.mirai.console.plugins.Config import net.mamoe.mirai.console.plugins.Config
import net.mamoe.mirai.console.plugins.ConfigSection 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.events.BotOnlineEvent
import net.mamoe.mirai.event.subscribeAlways import net.mamoe.mirai.event.subscribeAlways
import net.mamoe.mirai.event.subscribeMessages 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.data.Image
import net.mamoe.mirai.message.uploadAsImage import net.mamoe.mirai.message.uploadAsImage
import net.mamoe.mirai.utils.MiraiExperimentalAPI import net.mamoe.mirai.utils.MiraiExperimentalAPI
import org.jsoup.Jsoup import org.jsoup.Jsoup
import java.io.File import java.io.File
import kotlin.random.Random import java.net.URL
class ImageSenderMain : PluginBase() { class ImageSenderMain : PluginBase() {
@ -60,7 +60,6 @@ class ImageSenderMain : PluginBase() {
reply(e.message ?: "unknown error") reply(e.message ?: "unknown error")
} }
} }
} }
} }
} }
@ -84,16 +83,21 @@ class ImageSenderMain : PluginBase() {
override fun onLoad() { override fun onLoad() {
logger.info("loading local image data") logger.info("loading local image data")
try { try {
images = Config.load(this.javaClass.classLoader.getResource("data.yml")!!.path!!) images = Config.load(getResources(fileName = "data.yml")!!, "yml")
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace()
logger.info("无法加载本地图片") logger.info("无法加载本地图片")
} }
logger.info("本地图片版本" + images.getString("version")) logger.info("本地图片版本" + images.getString("version"))
logger.info("Normal * " + images.getList("normal").size) r18 = images.getConfigSectionList("R18")
logger.info("R18 * " + images.getList("R18").size) normal = images.getConfigSectionList("normal")
logger.info("Normal * " + normal.size)
logger.info("R18 * " + r18.size)
} }
override fun onDisable() { override fun onDisable() {
} }