From 108183daad8664c8897b002b85aa350eef5b1b6a Mon Sep 17 00:00:00 2001 From: "jiahua.liu" Date: Sun, 23 Feb 2020 20:09:01 +0800 Subject: [PATCH] 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() { }