From 0c58c511d0ed2d4d26e00ab034a7aa784007120c Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 18:22:58 +0800 Subject: [PATCH 1/8] Http api ignore unknown message type tdo --- .../net/mamoe/mirai/api/http/data/common/MessageDTO.kt | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) 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 d13a97f17..b0906898a 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 @@ -17,7 +17,9 @@ import net.mamoe.mirai.message.FriendMessage import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.MessagePacket import net.mamoe.mirai.message.data.* +import net.mamoe.mirai.message.uploadImage import net.mamoe.mirai.utils.MiraiInternalAPI +import java.net.URL /* * DTO data class @@ -97,7 +99,7 @@ fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply } fun MessageChainDTO.toMessageChain(contact: Contact) = - buildMessageChain { this@toMessageChain.forEach { add(it.toMessage(contact)) } } + buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } } @UseExperimental(ExperimentalUnsignedTypes::class) fun Message.toDTO() = when (this) { @@ -119,6 +121,6 @@ fun MessageDTO.toMessage(contact: Contact) = when (this) { is PlainDTO -> PlainText(text) is ImageDTO -> Image(imageId) is XmlDTO -> XMLMessage(xml) - is MessageSourceDTO, is UnknownMessageDTO -> PlainText("assert cannot reach") + is MessageSourceDTO, is UnknownMessageDTO -> null } From 94d859a4e6cf410d3e79d63f97ffa11a54771bbe Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 19:18:14 +0800 Subject: [PATCH 2/8] Http api better image with url --- .../mirai/api/http/data/common/BotEventDTO.kt | 2 +- .../mirai/api/http/data/common/MessageDTO.kt | 42 +++++++++++-------- .../mirai/api/http/queue/MessageQueue.kt | 2 +- .../api/http/route/MessageRouteModule.kt | 2 + 4 files changed, 28 insertions(+), 20 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/BotEventDTO.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/BotEventDTO.kt index e94a0c5e4..28630c173 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/BotEventDTO.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/data/common/BotEventDTO.kt @@ -12,7 +12,7 @@ import net.mamoe.mirai.utils.MiraiExperimentalAPI sealed class BotEventDTO : EventDTO() @UseExperimental(MiraiExperimentalAPI::class) -fun BotEvent.toDTO() = when(this) { +suspend fun BotEvent.toDTO() = when(this) { is MessagePacket<*, *> -> toDTO() else -> when(this) { is BotOnlineEvent -> BotOnlineEventDTO(bot.uin) 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 b0906898a..187c49a0e 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 @@ -58,7 +58,7 @@ data class PlainDTO(val text: String) : MessageDTO() @Serializable @SerialName("Image") -data class ImageDTO(val imageId: String) : MessageDTO() +data class ImageDTO(val imageId: String? = null, val url: String? = null) : MessageDTO() @Serializable @SerialName("Xml") @@ -85,41 +85,47 @@ sealed class MessageDTO : DTO /* Extend function */ -fun MessagePacket<*, *>.toDTO() = when (this) { +suspend fun MessagePacket<*, *>.toDTO() = when (this) { is FriendMessage -> FriendMessagePacketDTO(QQDTO(sender)) is GroupMessage -> GroupMessagePacketDTO(MemberDTO(sender)) else -> IgnoreEventDTO }.apply { - if (this is MessagePacketDTO) { messageChain = message.toDTOChain() } - // else: `this` is bot event + if (this is MessagePacketDTO) { + // 将MessagePacket中的所有Message转为DTO对象,并添加到messageChain + // foreachContent会忽略MessageSource,一次主动获取 + messageChain = mutableListOf(messageDTO(message[MessageSource])).apply { + message.foreachContent { content -> messageDTO(content).takeUnless { it == UnknownMessageDTO }?.let(::add) } + } + // else: `this` is bot event + } } -fun MessageChain.toDTOChain() = mutableListOf(this[MessageSource].toDTO()).apply { - foreachContent { content -> content.toDTO().takeUnless { it == UnknownMessageDTO }?.let(::add) } -} - -fun MessageChainDTO.toMessageChain(contact: Contact) = +suspend fun MessageChainDTO.toMessageChain(contact: Contact) = buildMessageChain { this@toMessageChain.forEach { it.toMessage(contact)?.let(::add) } } @UseExperimental(ExperimentalUnsignedTypes::class) -fun Message.toDTO() = when (this) { - is MessageSource -> MessageSourceDTO(id) - is At -> AtDTO(target, display) +suspend fun MessagePacket<*, *>.messageDTO(message: Message) = when (message) { + is MessageSource -> MessageSourceDTO(message.id) + is At -> AtDTO(message.target, message.display) is AtAll -> AtAllDTO(0L) - is Face -> FaceDTO(id) - is PlainText -> PlainDTO(stringValue) - is Image -> ImageDTO(imageId) - is XMLMessage -> XmlDTO(stringValue) + is Face -> FaceDTO(message.id) + is PlainText -> PlainDTO(message.stringValue) + is Image -> ImageDTO(message.imageId, message.url()) + is XMLMessage -> XmlDTO(message.stringValue) else -> UnknownMessageDTO } @UseExperimental(ExperimentalUnsignedTypes::class, MiraiInternalAPI::class) -fun MessageDTO.toMessage(contact: Contact) = when (this) { +suspend fun MessageDTO.toMessage(contact: Contact) = when (this) { is AtDTO -> At((contact as Group)[target]) is AtAllDTO -> AtAll is FaceDTO -> Face(faceId) is PlainDTO -> PlainText(text) - is ImageDTO -> Image(imageId) + is ImageDTO -> when { + !imageId.isNullOrBlank() -> Image(imageId) + !url.isNullOrBlank() -> contact.uploadImage(URL(url)) + else -> null + } is XmlDTO -> XMLMessage(xml) is MessageSourceDTO, is UnknownMessageDTO -> null } diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index e497d56e6..1cb1a54cb 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -23,7 +23,7 @@ class MessageQueue : ConcurrentLinkedDeque() { val quoteCacheSize = 4096 val quoteCache = LinkedHashMap() - fun fetch(size: Int): List { + suspend fun fetch(size: Int): List { var count = size val ret = ArrayList(count) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index 04631b2d1..e5b7b0bb7 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -46,12 +46,14 @@ fun Application.messageModule() { it.session.bot.getFriend(it.target).apply { sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ } + call.respondStateCode(StateCode.Success) } miraiVerify("/sendGroupMessage") { it.session.bot.getGroup(it.target).apply { sendMessage(it.messageChain.toMessageChain(this)) // this aka Group } + call.respondStateCode(StateCode.Success) } miraiVerify("/sendQuoteMessage") { From 108183daad8664c8897b002b85aa350eef5b1b6a Mon Sep 17 00:00:00 2001 From: "jiahua.liu" Date: Sun, 23 Feb 2020 20:09:01 +0800 Subject: [PATCH 3/8] demo plugin and read-only config --- .../mirai/console/plugins/ConfigSection.kt | 78 ++++++++++++++++--- .../mamoe/mirai/console/plugins/PluginBase.kt | 49 ++++++++++++ .../mirai/imageplugin/ImageSenderMain.kt | 18 +++-- 3 files changed, 128 insertions(+), 17 deletions(-) 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-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() { } From 0e5e2336ff9af79265dfe5b7b72acf0eb3704e74 Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 20:33:03 +0800 Subject: [PATCH 4/8] Http api return messageId after send a message --- mirai-api-http/README_CH.md | 20 ++++++++++------ .../api/http/route/MessageRouteModule.kt | 24 +++++++++++-------- .../net.mamoe.mirai/message/MessageReceipt.kt | 2 +- 3 files changed, 28 insertions(+), 18 deletions(-) diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index 274d7cd84..7019abd7d 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -175,12 +175,13 @@ fun main() { | target | Long | false | 987654321 | 发送消息目标好友的QQ号 | | messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | -#### 响应: 返回统一状态码 +#### 响应: 返回统一状态码(并携带messageId) ```json5 { "code": 0, - "msg": "success" + "msg": "success", + "messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复 } ``` @@ -213,12 +214,13 @@ fun main() { | target | Long | false | 987654321 | 发送消息目标群的群号 | | messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | -#### 响应: 返回统一状态码 +#### 响应: 返回统一状态码(并携带messageId) ```json5 { "code": 0, - "msg": "success" + "msg": "success", + "messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复 } ``` @@ -251,12 +253,13 @@ fun main() { | target | Long | false | 987654321 | 引用消息的Message Source的Uid | | messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | -#### 响应: 返回统一状态码 +#### 响应: 返回统一状态码(并携带messageId) ```json5 { "code": 0, - "msg": "success" + "msg": "success", + "messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复 } ``` @@ -370,7 +373,10 @@ Content-Type:multipart/form-data } },{ "type": "FriendMessage", // 消息类型:GroupMessage或FriendMessage或各类Event - "messageChain": [{ // 消息链,是一个消息对象构成的数组 + "messageChain": [{ // 消息链,是一个消息对象构成的数组 + "type": "Source", + "uid": 123456 + },{ "type": "Plain", "text": "Miral牛逼" }], diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index e5b7b0bb7..872d40497 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -20,10 +20,10 @@ import io.ktor.response.respondText import io.ktor.routing.post import io.ktor.routing.routing import kotlinx.serialization.Serializable -import kotlinx.serialization.Transient import net.mamoe.mirai.api.http.AuthedSession import net.mamoe.mirai.api.http.SessionManager import net.mamoe.mirai.api.http.data.* +import net.mamoe.mirai.api.http.data.common.DTO import net.mamoe.mirai.api.http.data.common.MessageChainDTO import net.mamoe.mirai.api.http.data.common.VerifyDTO import net.mamoe.mirai.api.http.data.common.toMessageChain @@ -44,23 +44,26 @@ fun Application.messageModule() { miraiVerify("/sendFriendMessage") { it.session.bot.getFriend(it.target).apply { - sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ + val receipt = sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ + receipt.source.ensureSequenceIdAvailable() + call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } - call.respondStateCode(StateCode.Success) } miraiVerify("/sendGroupMessage") { it.session.bot.getGroup(it.target).apply { - sendMessage(it.messageChain.toMessageChain(this)) // this aka Group + val receipt = sendMessage(it.messageChain.toMessageChain(this)) // this aka Group + receipt.source.ensureSequenceIdAvailable() + call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } - call.respondStateCode(StateCode.Success) } miraiVerify("/sendQuoteMessage") { it.session.messageQueue.quoteCache[it.target]?.apply { - quoteReply(it.messageChain.toMessageChain(group)) + val receipt = quoteReply(it.messageChain.toMessageChain(group)) + receipt.source.ensureSequenceIdAvailable() + call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } ?: throw NoSuchElementException() - call.respondStateCode(StateCode.Success) } miraiVerify("sendImageMessage") { @@ -127,9 +130,10 @@ private data class SendImageDTO( @Serializable private class SendRetDTO( - val messageId: Long, - @Transient val stateCode: StateCode = Success -) : StateCode(stateCode.code, stateCode.msg) + val code: Int = 0, + val msg: String = "success", + val messageId: Long +) : DTO @Serializable private data class RecallDTO( diff --git a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt index 7d5c37657..31482e393 100644 --- a/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt +++ b/mirai-core/src/commonMain/kotlin/net.mamoe.mirai/message/MessageReceipt.kt @@ -31,7 +31,7 @@ import net.mamoe.mirai.utils.unsafeWeakRef * @see QQ.sendMessage 发送群消息, 返回回执(此对象) */ open class MessageReceipt( - private val source: MessageSource, + val source: MessageSource, target: C ) { init { From a52655e2abe32d6b31dd34eb153e754de852e1aa Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 22:57:06 +0800 Subject: [PATCH 5/8] Fix error in LockFreeLinkedListTest --- .../kotlin/net/mamoe/mirai/utils/LockFreeLinkedListTest.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 de4c3b195..6af63aa89 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 @@ -161,7 +161,7 @@ internal class LockFreeLinkedListTest { println("Check value") value shouldBeEqualTo 6 println("Check size") - println(list.getLinkStructure()) +// println(list.getLinkStructure()) list.size shouldBeEqualTo 6 } @@ -174,7 +174,7 @@ internal class LockFreeLinkedListTest { println("Check value") value shouldBeEqualTo 2 println("Check size") - println(list.getLinkStructure()) +// println(list.getLinkStructure()) list.size shouldBeEqualTo 5 } @@ -198,7 +198,7 @@ internal class LockFreeLinkedListTest { println("Check value") value shouldBeEqualTo 2 println("Check size") - println(list.getLinkStructure()) +// println(list.getLinkStructure()) list.size shouldBeEqualTo 1 } /* From cd73d7f53467407cc3c9b277a39decf391f02e45 Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 22:58:24 +0800 Subject: [PATCH 6/8] Http api friendly quote --- .../mirai/api/http/queue/MessageQueue.kt | 11 +++-- .../api/http/route/MessageRouteModule.kt | 40 ++++++++++++++----- 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index 1cb1a54cb..83250127a 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -14,6 +14,7 @@ import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.event.events.BotEvent import net.mamoe.mirai.message.GroupMessage +import net.mamoe.mirai.message.MessagePacket import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.firstKey import java.util.concurrent.ConcurrentLinkedDeque @@ -21,7 +22,7 @@ import java.util.concurrent.ConcurrentLinkedDeque class MessageQueue : ConcurrentLinkedDeque() { val quoteCacheSize = 4096 - val quoteCache = LinkedHashMap() + val quoteCache = LinkedHashMap>() suspend fun fetch(size: Int): List { var count = size @@ -37,15 +38,17 @@ class MessageQueue : ConcurrentLinkedDeque() { } } - // TODO: 等FriendMessage支持quote - if (event is GroupMessage) { + if (event is MessagePacket<*, *>) { addQuoteCache(event) } } return ret } - private fun addQuoteCache(msg: GroupMessage) { + fun cacheQuote(messageId: Long) = + quoteCache[messageId] ?: throw NoSuchElementException() + + private fun addQuoteCache(msg: MessagePacket<*, *>) { quoteCache[msg.message[MessageSource].id] = msg if (quoteCache.size > quoteCacheSize) { quoteCache.remove(quoteCache.firstKey()) diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index 872d40497..7e59e6e43 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -28,7 +28,9 @@ import net.mamoe.mirai.api.http.data.common.MessageChainDTO import net.mamoe.mirai.api.http.data.common.VerifyDTO import net.mamoe.mirai.api.http.data.common.toMessageChain import net.mamoe.mirai.api.http.util.toJson -import net.mamoe.mirai.message.data.MessageChain +import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.message.MessageReceipt +import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.uploadImage import java.net.URL @@ -42,30 +44,45 @@ fun Application.messageModule() { call.respondJson(fetch.toJson()) } + suspend fun sendMessage( + quote: QuoteReplyToSend?, + messageChain: MessageChain, + target: C + ): MessageReceipt { + val send = if (quote == null) { + messageChain + } else { + (quote + messageChain).toChain() + } + return target.sendMessage(send) + } + miraiVerify("/sendFriendMessage") { + val quote = it.quote?.let { q -> + it.session.messageQueue.cacheQuote(q).run { + this[MessageSource].quote(sender) + }} + it.session.bot.getFriend(it.target).apply { - val receipt = sendMessage(it.messageChain.toMessageChain(this)) // this aka QQ + val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this) receipt.source.ensureSequenceIdAvailable() call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } } miraiVerify("/sendGroupMessage") { + val quote = it.quote?.let { q -> + it.session.messageQueue.cacheQuote(q).run { + this[MessageSource].quote(sender) + }} + it.session.bot.getGroup(it.target).apply { - val receipt = sendMessage(it.messageChain.toMessageChain(this)) // this aka Group + val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this) receipt.source.ensureSequenceIdAvailable() call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } } - miraiVerify("/sendQuoteMessage") { - it.session.messageQueue.quoteCache[it.target]?.apply { - val receipt = quoteReply(it.messageChain.toMessageChain(group)) - receipt.source.ensureSequenceIdAvailable() - call.respondDTO(SendRetDTO(messageId = receipt.source.id)) - } ?: throw NoSuchElementException() - } - miraiVerify("sendImageMessage") { val bot = it.session.bot val contact = when { @@ -115,6 +132,7 @@ fun Application.messageModule() { @Serializable private data class SendDTO( override val sessionKey: String, + val quote: Long? = null, val target: Long, val messageChain: MessageChainDTO ) : VerifyDTO() From 0d7af4a111f8ce521a1d82c2fce660999c6b8e2b Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 23:01:41 +0800 Subject: [PATCH 7/8] Http api update README_CH.md --- mirai-api-http/README_CH.md | 41 ++----------------------------------- 1 file changed, 2 insertions(+), 39 deletions(-) diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index 7019abd7d..30f52fce4 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -173,6 +173,7 @@ fun main() { | ------------ | ------ | ----- | ----------- | -------------------------------- | | sessionKey | String | false | YourSession | 已经激活的Session | | target | Long | false | 987654321 | 发送消息目标好友的QQ号 | +| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 | | messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | #### 响应: 返回统一状态码(并携带messageId) @@ -212,45 +213,7 @@ fun main() { | ------------ | ------ | ----- | ----------- | -------------------------------- | | sessionKey | String | false | YourSession | 已经激活的Session | | target | Long | false | 987654321 | 发送消息目标群的群号 | -| messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | - -#### 响应: 返回统一状态码(并携带messageId) - -```json5 -{ - "code": 0, - "msg": "success", - "messageId": 1234567890 // 一个Long类型属性,标识本条消息,用于撤回和引用回复 -} -``` - - - -### 发送引用回复消息(仅支持群消息) - -``` -[POST] /sendQuoteMessage -``` - -使用此方法向指定的消息进行引用回复 - -#### 请求 - -```json5 -{ - "sessionKey": "YourSession", - "target": 987654321, - "messageChain": [ - { "type": "Plain", "text":"hello\n" }, - { "type": "Plain", "text":"world" } - ] -} -``` - -| 名字 | 类型 | 可选 | 举例 | 说明 | -| ------------ | ------ | ----- | ----------- | -------------------------------- | -| sessionKey | String | false | YourSession | 已经激活的Session | -| target | Long | false | 987654321 | 引用消息的Message Source的Uid | +| quote | Long | true | 135798642 | 引用一条消息的messageId进行回复 | | messageChain | Array | false | [] | 消息链,是一个消息对象构成的数组 | #### 响应: 返回统一状态码(并携带messageId) From f4020e4a03a6868ac358dc665bbbf4e4beb3703d Mon Sep 17 00:00:00 2001 From: ryoii Date: Sun, 23 Feb 2020 23:58:01 +0800 Subject: [PATCH 8/8] Http api support recall --- mirai-api-http/README_CH.md | 33 +++++++++++++++++++ .../mirai/api/http/queue/MessageQueue.kt | 17 +++++----- .../api/http/route/MessageRouteModule.kt | 19 +++++++---- 3 files changed, 54 insertions(+), 15 deletions(-) diff --git a/mirai-api-http/README_CH.md b/mirai-api-http/README_CH.md index 30f52fce4..ff595681d 100644 --- a/mirai-api-http/README_CH.md +++ b/mirai-api-http/README_CH.md @@ -297,6 +297,39 @@ Content-Type:multipart/form-data +### 撤回消息 + +``` +[POST] /recall +``` + +使用此方法撤回指定消息。对于bot发送的消息,又2分钟时间限制。对于撤回群聊中群员的消息,需要有相应权限 + +#### 请求 + +```json5 +{ + "sessionKey": "YourSession", + "target": 987654321 +} +``` + +| 名字 | 类型 | 可选 | 举例 | 说明 | +| ------------ | ------ | ----- | ----------- | -------------------------------- | +| sessionKey | String | false | YourSession | 已经激活的Session | +| target | Long | false | 987654321 | 需要撤回的消息的messageId | + +#### 响应: 返回统一状态码 + +```json5 +{ + "code": 0, + "msg": "success" +} +``` + + + ### 获取Bot收到的消息和事件 ``` diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt index 83250127a..f39a3e8e8 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/queue/MessageQueue.kt @@ -13,7 +13,6 @@ import net.mamoe.mirai.api.http.data.common.EventDTO import net.mamoe.mirai.api.http.data.common.IgnoreEventDTO import net.mamoe.mirai.api.http.data.common.toDTO import net.mamoe.mirai.event.events.BotEvent -import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.MessagePacket import net.mamoe.mirai.message.data.MessageSource import net.mamoe.mirai.utils.firstKey @@ -21,8 +20,8 @@ import java.util.concurrent.ConcurrentLinkedDeque class MessageQueue : ConcurrentLinkedDeque() { - val quoteCacheSize = 4096 - val quoteCache = LinkedHashMap>() + val cacheSize = 4096 + val cache = LinkedHashMap>() suspend fun fetch(size: Int): List { var count = size @@ -45,13 +44,13 @@ class MessageQueue : ConcurrentLinkedDeque() { return ret } - fun cacheQuote(messageId: Long) = - quoteCache[messageId] ?: throw NoSuchElementException() + fun cache(messageId: Long) = + cache[messageId] ?: throw NoSuchElementException() - private fun addQuoteCache(msg: MessagePacket<*, *>) { - quoteCache[msg.message[MessageSource].id] = msg - if (quoteCache.size > quoteCacheSize) { - quoteCache.remove(quoteCache.firstKey()) + fun addQuoteCache(msg: MessagePacket<*, *>) { + cache[msg.message[MessageSource].id] = msg + if (cache.size > cacheSize) { + cache.remove(cache.firstKey()) } } } \ No newline at end of file diff --git a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt index 7e59e6e43..46a555a99 100644 --- a/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt +++ b/mirai-api-http/src/main/kotlin/net/mamoe/mirai/api/http/route/MessageRouteModule.kt @@ -29,6 +29,8 @@ import net.mamoe.mirai.api.http.data.common.VerifyDTO import net.mamoe.mirai.api.http.data.common.toMessageChain import net.mamoe.mirai.api.http.util.toJson import net.mamoe.mirai.contact.Contact +import net.mamoe.mirai.message.FriendMessage +import net.mamoe.mirai.message.GroupMessage import net.mamoe.mirai.message.MessageReceipt import net.mamoe.mirai.message.data.* import net.mamoe.mirai.message.uploadImage @@ -59,26 +61,30 @@ fun Application.messageModule() { miraiVerify("/sendFriendMessage") { val quote = it.quote?.let { q -> - it.session.messageQueue.cacheQuote(q).run { + it.session.messageQueue.cache(q).run { this[MessageSource].quote(sender) }} it.session.bot.getFriend(it.target).apply { val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this) receipt.source.ensureSequenceIdAvailable() + + it.session.messageQueue.addQuoteCache(FriendMessage(bot.selfQQ, receipt.source.toChain())) call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } } miraiVerify("/sendGroupMessage") { val quote = it.quote?.let { q -> - it.session.messageQueue.cacheQuote(q).run { + it.session.messageQueue.cache(q).run { this[MessageSource].quote(sender) }} it.session.bot.getGroup(it.target).apply { val receipt = sendMessage(quote, it.messageChain.toMessageChain(this), this) receipt.source.ensureSequenceIdAvailable() + + it.session.messageQueue.addQuoteCache(GroupMessage("", botPermission, botAsMember, receipt.source.toChain())) call.respondDTO(SendRetDTO(messageId = receipt.source.id)) } } @@ -123,8 +129,10 @@ fun Application.messageModule() { } miraiVerify("recall") { - // TODO - call.respond(HttpStatusCode.NotFound, "未完成") + it.session.messageQueue.cache(it.target).apply { + it.session.bot.recall(get(MessageSource)) + } + call.respondStateCode(StateCode.Success) } } } @@ -156,6 +164,5 @@ private class SendRetDTO( @Serializable private data class RecallDTO( override val sessionKey: String, - val target: Long, - val sender: Long + val target: Long ) : VerifyDTO()