diff --git a/project/MornyConfiguration.scala b/project/MornyConfiguration.scala index eb9ac72..2f19815 100644 --- a/project/MornyConfiguration.scala +++ b/project/MornyConfiguration.scala @@ -19,7 +19,7 @@ object MornyConfiguration { "com.github.spotbugs" % "spotbugs-annotations" % "4.8.4" % Compile, "cc.sukazyo" % "messiva" % "0.2.0", - "cc.sukazyo" % "resource-tools" % "0.2.2", + "cc.sukazyo" % "resource-tools" % "0.3.0", "com.github.pengrad" % "java-telegram-bot-api" % "6.2.0", "org.http4s" %% "http4s-dsl" % "0.23.27", diff --git a/src/main/resources/assets_morny/images/featured-image@0.5x.jpg b/src/main/resources/assets/morny-coeur/images/featured-image@0.5x.jpg similarity index 100% rename from src/main/resources/assets_morny/images/featured-image@0.5x.jpg rename to src/main/resources/assets/morny-coeur/images/featured-image@0.5x.jpg diff --git a/src/main/resources/assets_morny/images/http-sekai-400.png b/src/main/resources/assets/morny-coeur/images/http-sekai-400.png similarity index 100% rename from src/main/resources/assets_morny/images/http-sekai-400.png rename to src/main/resources/assets/morny-coeur/images/http-sekai-400.png diff --git a/src/main/resources/assets_morny/images/http-sekai-404.png b/src/main/resources/assets/morny-coeur/images/http-sekai-404.png similarity index 100% rename from src/main/resources/assets_morny/images/http-sekai-404.png rename to src/main/resources/assets/morny-coeur/images/http-sekai-404.png diff --git a/src/main/resources/assets_morny/images/http-sekai-500.png b/src/main/resources/assets/morny-coeur/images/http-sekai-500.png similarity index 100% rename from src/main/resources/assets_morny/images/http-sekai-500.png rename to src/main/resources/assets/morny-coeur/images/http-sekai-500.png diff --git a/src/main/resources/assets_morny/images/http-sekai-501.png b/src/main/resources/assets/morny-coeur/images/http-sekai-501.png similarity index 100% rename from src/main/resources/assets_morny/images/http-sekai-501.png rename to src/main/resources/assets/morny-coeur/images/http-sekai-501.png diff --git a/src/main/resources/assets_morny/images/http-sekai-523.png b/src/main/resources/assets/morny-coeur/images/http-sekai-523.png similarity index 100% rename from src/main/resources/assets_morny/images/http-sekai-523.png rename to src/main/resources/assets/morny-coeur/images/http-sekai-523.png diff --git a/src/main/resources/assets_morny/langs/_index.hyl b/src/main/resources/assets/morny-coeur/langs/_index.hyl similarity index 100% rename from src/main/resources/assets_morny/langs/_index.hyl rename to src/main/resources/assets/morny-coeur/langs/_index.hyl diff --git a/src/main/resources/assets_morny/langs/en_us.hyt b/src/main/resources/assets/morny-coeur/langs/en_us.hyt similarity index 100% rename from src/main/resources/assets_morny/langs/en_us.hyt rename to src/main/resources/assets/morny-coeur/langs/en_us.hyt diff --git a/src/main/resources/assets_morny/langs/zh_cn.hyt b/src/main/resources/assets/morny-coeur/langs/zh_cn.hyt similarity index 100% rename from src/main/resources/assets_morny/langs/zh_cn.hyt rename to src/main/resources/assets/morny-coeur/langs/zh_cn.hyt diff --git a/src/main/resources/assets_morny/langs/zh_tw.hyt b/src/main/resources/assets/morny-coeur/langs/zh_tw.hyt similarity index 100% rename from src/main/resources/assets_morny/langs/zh_tw.hyt rename to src/main/resources/assets/morny-coeur/langs/zh_tw.hyt diff --git a/src/main/resources/assets/morny-coeur/morny-coeur.identifier b/src/main/resources/assets/morny-coeur/morny-coeur.identifier new file mode 100644 index 0000000..e69de29 diff --git a/src/main/resources/assets_morny/texts/server-hello.txt b/src/main/resources/assets/morny-coeur/texts/server-hello.txt similarity index 100% rename from src/main/resources/assets_morny/texts/server-hello.txt rename to src/main/resources/assets/morny-coeur/texts/server-hello.txt diff --git a/src/main/resources/morny-modules.list b/src/main/resources/morny-modules.list new file mode 100644 index 0000000..b6b93e5 --- /dev/null +++ b/src/main/resources/morny-modules.list @@ -0,0 +1,14 @@ +cc.sukazyo.cono.morny.tele_utils.ModuleTeleUtils +cc.sukazyo.cono.morny.randomize_somthing.ModuleRandomize +cc.sukazyo.cono.morny.slash_action.ModuleSlashAction +cc.sukazyo.cono.morny.nbnhhsh.ModuleNbnhhsh +cc.sukazyo.cono.morny.ip186.ModuleIP186 +cc.sukazyo.cono.morny.crons.ModuleCRONs +cc.sukazyo.cono.morny.encrypt_tool.ModuleEncryptor +cc.sukazyo.cono.morny.call_me.ModuleCallMe +cc.sukazyo.cono.morny.social_share.ModuleSocialShare +cc.sukazyo.cono.morny.medication_timer.ModuleMedicationTimer +cc.sukazyo.cono.morny.morny_misc.ModuleMornyMisc +cc.sukazyo.cono.morny.uni_meow.ModuleUniMeow +cc.sukazyo.cono.morny.reporter.Module +cc.sukazyo.cono.morny.stickers_get.Module diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyAbout.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyAbout.scala index 52113dd..9c6b2bb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyAbout.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyAbout.scala @@ -7,7 +7,7 @@ import java.io.IOException object MornyAbout { val MORNY_PREVIEW_IMAGE_ASCII: String = - try { MornyAssets.pack `getResource` "texts/server-hello.txt" readAsString } + try { MornyAssets.assets.getFile("texts/server-hello.txt").readString } catch case e: IOException => throw RuntimeException("Cannot read MORNY_PREVIEW_IMAGE_ASCII from assets pack", e) diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala index ff5e9d2..e15cfa9 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala @@ -8,6 +8,7 @@ import cc.sukazyo.cono.morny.core.bot.event.{MornyOnInlineQuery, MornyOnTelegram import cc.sukazyo.cono.morny.core.bot.internal.{ErrorMessageManager, ThreadingManagerImpl} import cc.sukazyo.cono.morny.core.http.api.{HttpServer, MornyHttpServerContext} import cc.sukazyo.cono.morny.core.http.internal.MornyHttpServerContextImpl +import cc.sukazyo.cono.morny.core.module.ModuleHelper import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.util.schedule.Scheduler import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis @@ -15,6 +16,7 @@ import cc.sukazyo.cono.morny.util.time.WatchDog import cc.sukazyo.cono.morny.util.GivenContext import cc.sukazyo.cono.morny.util.UseString.MString import cc.sukazyo.cono.morny.util.UseThrowable.toLogString +import cc.sukazyo.cono.morny.util.dataview.Table import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.request.GetMe @@ -118,13 +120,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes given MornyCoeur = this val externalContext: GivenContext = GivenContext() - import cc.sukazyo.cono.morny.util.dataview.Table.format as fmtTable logger `info` m"""The following Modules have been added to current Morny: - |${fmtTable( - "Module ID" :: "Module Name" :: "Module Version" :: Nil, - modules.map(f => f.id :: f.name :: f.version :: Nil)* - )} + |${ModuleHelper.drawTable(modules)} |""" ///>>> BLOCK START local storage / data configuration diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala index 14a980a..40e9dfe 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyLangs.scala @@ -22,8 +22,8 @@ object MornyLangs { val (lang_dir, lang_index_content) = try { ( - MornyAssets.pack.getResDir("langs"), - MornyAssets.pack.getResource("langs/_index.hyl").readAsString() + MornyAssets.assets.getDirectory("langs"), + MornyAssets.assets.getFile("langs/_index.hyl").readString ) } catch case e: IOException => throw Exception("Cannot read Morny's translations file.", e) @@ -37,11 +37,10 @@ object MornyLangs { val language_translations = mutable.HashMap.empty[String, Definitions] - for (file <- lang_dir.listFiles().filter(_.isFile)) yield { + for (file <- lang_dir.listFiles()) yield { boundary { - import file.getPath as raw_path - if !(raw_path.endsWith(".hyt") || raw_path.endsWith(".hytrans")) then break() - val file_name = file.getPath.reverse.takeWhile(c => (c != '/') && (c != '\\')).reverse + val file_name = file.getPath.last + if !(file_name.endsWith(".hyt") || file_name.endsWith(".hytrans")) then break() val file_basename = file_name.dropRight( if file_name.endsWith(".hyt") then ".hyt".length else ".hytrans".length @@ -51,7 +50,7 @@ object MornyLangs { logger `warn` s"translation file \"$file_name\" is not in language index, so it got ignored (normalized lang name is \"$normalized\")." break() val lang_def = try { - val content = file.readAsString() + val content = file.readString Parser.parse(content) } catch case e: IOException => logger `error` diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala b/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala index 3633239..ffd4ea8 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala @@ -1,7 +1,8 @@ package cc.sukazyo.cono.morny.core import cc.sukazyo.cono.morny.core.Log.logger -import cc.sukazyo.cono.morny.core.MornyConfig.CheckFailure +import cc.sukazyo.cono.morny.core.MornyConfig.{CheckFailure, PROP_TOKEN_KEY} +import cc.sukazyo.cono.morny.core.module.{ModuleHelper, ModuleLoader} import cc.sukazyo.cono.morny.util.CommonFormat import java.time.ZoneOffset @@ -19,6 +20,7 @@ object ServerMain { val config = new MornyConfig.Prototype() var mode_echoVersion = false + var mode_echoModules = false var mode_echoHello = false var mode_testRun = false var showHello = true @@ -45,6 +47,7 @@ object ServerMain { case "--no-hello" | "-hf" | "--quiet" | "-q" => showHello = false case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true case "--version" | "-v" => mode_echoVersion = true + case "--modules" | "-mod" => mode_echoModules = true // deprecated: use --outdated-ignore instead // case "--outdated-block" | "-ob" => @@ -167,6 +170,8 @@ object ServerMain { s"""The Skip Login feature is not implemented yet! |""".stripMargin + val loadedModules = ModuleLoader.loadCoreModules() + if (mode_echoVersion) { logger `info` @@ -195,10 +200,18 @@ object ServerMain { | ${MornySystem.CODE_TIMESTAMP} | ${CommonFormat.formatDate(MornySystem.CODE_TIMESTAMP, 0)} [UTC]""" .stripMargin - return } + if (mode_echoModules) { + + logger `info` s"Loaded modules ::: \n${ModuleHelper.drawTable(loadedModules)}" + + } + + if mode_echoVersion | mode_echoModules then + return + logger `info` s"""ServerMain.java Loaded >>> |- version ${MornySystem.VERSION_FULL} @@ -222,7 +235,7 @@ object ServerMain { try MornyCoeur( - ServerModulesLoader.load() + loadedModules )(using config build)( testRun = mode_testRun ) diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala b/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala deleted file mode 100644 index e07beed..0000000 --- a/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala +++ /dev/null @@ -1,30 +0,0 @@ -package cc.sukazyo.cono.morny.core - -import cc.sukazyo.cono.morny - -object ServerModulesLoader { - - def load (): List[MornyModule] = { - - List( - - morny.tele_utils.ModuleTeleUtils(), - morny.randomize_somthing.ModuleRandomize(), - morny.slash_action.ModuleSlashAction(), - morny.nbnhhsh.ModuleNbnhhsh(), - morny.ip186.ModuleIP186(), - morny.crons.ModuleCRONs(), - morny.encrypt_tool.ModuleEncryptor(), - morny.call_me.ModuleCallMe(), - morny.social_share.ModuleSocialShare(), - morny.medication_timer.ModuleMedicationTimer(), - morny.morny_misc.ModuleMornyMisc(), - morny.uni_meow.ModuleUniMeow(), - morny.reporter.Module(), - morny.stickers_get.Module(), - - ) - - } - -} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleHelper.scala b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleHelper.scala new file mode 100644 index 0000000..5569423 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleHelper.scala @@ -0,0 +1,15 @@ +package cc.sukazyo.cono.morny.core.module + +import cc.sukazyo.cono.morny.core.MornyModule +import cc.sukazyo.cono.morny.util.dataview.Table + +object ModuleHelper { + + def drawTable (modules: List[MornyModule]): String = { + Table.format( + "Module ID" :: "Module Name" :: "Module Version" :: Nil, + modules.map(f => f.id :: f.name :: f.version :: Nil) * + ) + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleLoader.scala b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleLoader.scala new file mode 100644 index 0000000..1464f84 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModuleLoader.scala @@ -0,0 +1,51 @@ +package cc.sukazyo.cono.morny.core.module + +import cc.sukazyo.cono.morny.core.{MornyCoeur, MornyModule} +import cc.sukazyo.cono.morny.core.Log.logger +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString + +import java.nio.charset.StandardCharsets +import scala.collection.mutable.ListBuffer + +object ModuleLoader { + + def loadCoreModules (): List[MornyModule] = { + loadFromJar(classOf[MornyCoeur]) + } + + def loadFromJar (packageClazz: Class[?]): List[MornyModule] = { + val list = ListBuffer[MornyModule]() + + val moduleListFile = packageClazz.getResourceAsStream("/morny-modules.list") + .readAllBytes() + val modules = String(moduleListFile, StandardCharsets.UTF_8) + .split("\n") + .map(_.strip) + .filter(_.nonEmpty) + + modules.foreach { (clazzName: String) => + try { + val clazz = Class.forName(clazzName) + val instance = clazz.getConstructor().newInstance() + instance match + case module: MornyModule => + list += module + case _ => + logger `error` + s"""Module is not a Morny Module : + | - in package : ${packageClazz.getName} + | - declared class name : $clazzName + |You need to implement a MornyModule trait to make it a REAL morny module!""".stripMargin + } catch case e: Exception => + logger `error` + s"""Failed to create a module instance : + | - in package : ${packageClazz.getName} + | - declared class name : $clazzName + |${e.toLogString} + |Is this a typo or packaging error? You need to add morny-modules.list and your code to the same jar.""".stripMargin + } + + list.toList + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/module/ModulesJarLoader.scala b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModulesJarLoader.scala new file mode 100644 index 0000000..236492e --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/core/module/ModulesJarLoader.scala @@ -0,0 +1,9 @@ +package cc.sukazyo.cono.morny.core.module + +object ModulesJarLoader { + + def load (): Unit = { + // TODO + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyAssets.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyAssets.scala index c5e4c92..e37c228 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/MornyAssets.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyAssets.scala @@ -1,11 +1,14 @@ package cc.sukazyo.cono.morny.data -import cc.sukazyo.restools.ResourcesPackage +import cc.sukazyo.restools.{ResourceDirectory, ResourcePackage} object MornyAssets { class AssetsException (caused: Throwable) extends Exception("Cannot read assets file.", caused) - val pack: ResourcesPackage = ResourcesPackage(MornyAssets.getClass, "assets_morny") + val assetsLocation: List[String] = "assets" :: "morny-coeur" :: Nil + + val pack: ResourcePackage = ResourcePackage.get(assetsLocation :+ "morny-coeur.identifier" *) + val assets: ResourceDirectory = pack.getDirectory(assetsLocation*) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala index 571559a..7b4d6f4 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala @@ -37,7 +37,7 @@ object MornyInformation { catch case _: UnknownHostException => None } - def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get + def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT.get def getMornyAboutLinksVars: List[Var] = List( diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala index 5a7110b..2ea491a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala @@ -8,7 +8,7 @@ import scala.util.Using object TelegramImages { - class AssetsFileImage (assetsPath: String) { + class AssetsFileImage (assetsPath: List[String]) { private var cache: Option[Array[Byte]] = None @@ -19,7 +19,7 @@ object TelegramImages { @throws[AssetsException] private def read (): Unit = { - Using ((MornyAssets.pack `getResource` assetsPath)read) { stream => + Using (MornyAssets.assets.getFile(assetsPath*).read()) { stream => try { this.cache = Some(stream.readAllBytes()) } catch case e: IOException => { throw AssetsException(e) @@ -29,11 +29,18 @@ object TelegramImages { } - val IMG_ABOUT: AssetsFileImage = AssetsFileImage("images/featured-image@0.5x.jpg") - val IMG_400: AssetsFileImage = AssetsFileImage("images/http-sekai-400.png") - val IMG_404: AssetsFileImage = AssetsFileImage("images/http-sekai-404.png") - val IMG_500: AssetsFileImage = AssetsFileImage("images/http-sekai-500.png") - val IMG_501: AssetsFileImage = AssetsFileImage("images/http-sekai-501.png") - val IMG_523: AssetsFileImage = AssetsFileImage("images/http-sekai-523.png") + object AssetsFileImage { + def byId (id: String): AssetsFileImage = + byId(id, "png") + def byId (id: String, ty: String): AssetsFileImage = + AssetsFileImage("images" :: s"$id.$ty" :: Nil); + } + + val IMG_ABOUT: AssetsFileImage = AssetsFileImage.byId("featured-image@0.5x", "jpg") + val IMG_400: AssetsFileImage = AssetsFileImage.byId("http-sekai-400") + val IMG_404: AssetsFileImage = AssetsFileImage.byId("http-sekai-404") + val IMG_500: AssetsFileImage = AssetsFileImage.byId("http-sekai-500") + val IMG_501: AssetsFileImage = AssetsFileImage.byId("http-sekai-501") + val IMG_523: AssetsFileImage = AssetsFileImage.byId("http-sekai-523") } diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/MornyTests.scala b/src/test/scala/cc/sukazyo/cono/morny/test/MornyTests.scala index dd1fc9f..dbbe88a 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/MornyTests.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/MornyTests.scala @@ -1,13 +1,13 @@ package cc.sukazyo.cono.morny.test -import cc.sukazyo.restools.ResourcesPackage +import cc.sukazyo.restools.{ResourceDirectory, ResourcePackage} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should abstract class MornyTests extends AnyFreeSpec with should.Matchers { - val assets: ResourcesPackage = - ResourcesPackage(classOf[MornyTests], "assets_morny_tests") + val pack: ResourcePackage = ResourcePackage.get("assets_morny_tests") + val assets: ResourceDirectory = pack.getDirectory("assets_morny_tests") val pending_val = "[not-implemented]" diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/utils/CommonEncryptTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/utils/CommonEncryptTest.scala index 06f5c38..c4f6139 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/utils/CommonEncryptTest.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/utils/CommonEncryptTest.scala @@ -83,7 +83,7 @@ class CommonEncryptTest extends MornyTests with TableDrivenPropertyChecks { if file == null then Array.empty[Byte] else - assets.getResource(file).read.readAllBytes + assets.getFile(file).read.readAllBytes s"while hashing binary $_name :" - {