add file-based module loader and update resource-tools to 0.3.0

This commit is contained in:
A.C.Sukazyo Eyre 2024-07-03 14:50:33 +08:00
parent 11542377d5
commit 388351593a
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
27 changed files with 141 additions and 62 deletions

View File

@ -19,7 +19,7 @@ object MornyConfiguration {
"com.github.spotbugs" % "spotbugs-annotations" % "4.8.4" % Compile, "com.github.spotbugs" % "spotbugs-annotations" % "4.8.4" % Compile,
"cc.sukazyo" % "messiva" % "0.2.0", "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", "com.github.pengrad" % "java-telegram-bot-api" % "6.2.0",
"org.http4s" %% "http4s-dsl" % "0.23.27", "org.http4s" %% "http4s-dsl" % "0.23.27",

View File

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

View File

Before

Width:  |  Height:  |  Size: 73 KiB

After

Width:  |  Height:  |  Size: 73 KiB

View File

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 90 KiB

View File

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 80 KiB

View File

@ -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

View File

@ -7,7 +7,7 @@ import java.io.IOException
object MornyAbout { object MornyAbout {
val MORNY_PREVIEW_IMAGE_ASCII: String = 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 => catch case e: IOException =>
throw RuntimeException("Cannot read MORNY_PREVIEW_IMAGE_ASCII from assets pack", e) throw RuntimeException("Cannot read MORNY_PREVIEW_IMAGE_ASCII from assets pack", e)

View File

@ -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.bot.internal.{ErrorMessageManager, ThreadingManagerImpl}
import cc.sukazyo.cono.morny.core.http.api.{HttpServer, MornyHttpServerContext} 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.http.internal.MornyHttpServerContextImpl
import cc.sukazyo.cono.morny.core.module.ModuleHelper
import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.reporter.MornyReport
import cc.sukazyo.cono.morny.util.schedule.Scheduler import cc.sukazyo.cono.morny.util.schedule.Scheduler
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis 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.GivenContext
import cc.sukazyo.cono.morny.util.UseString.MString import cc.sukazyo.cono.morny.util.UseString.MString
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString 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.TelegramBot
import com.pengrad.telegrambot.request.GetMe import com.pengrad.telegrambot.request.GetMe
@ -118,13 +120,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
given MornyCoeur = this given MornyCoeur = this
val externalContext: GivenContext = GivenContext() val externalContext: GivenContext = GivenContext()
import cc.sukazyo.cono.morny.util.dataview.Table.format as fmtTable
logger `info` logger `info`
m"""The following Modules have been added to current Morny: m"""The following Modules have been added to current Morny:
|${fmtTable( |${ModuleHelper.drawTable(modules)}
"Module ID" :: "Module Name" :: "Module Version" :: Nil,
modules.map(f => f.id :: f.name :: f.version :: Nil)*
)}
|""" |"""
///>>> BLOCK START local storage / data configuration ///>>> BLOCK START local storage / data configuration

View File

@ -22,8 +22,8 @@ object MornyLangs {
val (lang_dir, lang_index_content) = try { val (lang_dir, lang_index_content) = try {
( (
MornyAssets.pack.getResDir("langs"), MornyAssets.assets.getDirectory("langs"),
MornyAssets.pack.getResource("langs/_index.hyl").readAsString() MornyAssets.assets.getFile("langs/_index.hyl").readString
) )
} catch case e: IOException => } catch case e: IOException =>
throw Exception("Cannot read Morny's translations file.", e) throw Exception("Cannot read Morny's translations file.", e)
@ -37,11 +37,10 @@ object MornyLangs {
val language_translations = mutable.HashMap.empty[String, Definitions] val language_translations = mutable.HashMap.empty[String, Definitions]
for (file <- lang_dir.listFiles().filter(_.isFile)) yield { for (file <- lang_dir.listFiles()) yield {
boundary { boundary {
import file.getPath as raw_path val file_name = file.getPath.last
if !(raw_path.endsWith(".hyt") || raw_path.endsWith(".hytrans")) then break() if !(file_name.endsWith(".hyt") || file_name.endsWith(".hytrans")) then break()
val file_name = file.getPath.reverse.takeWhile(c => (c != '/') && (c != '\\')).reverse
val file_basename = file_name.dropRight( val file_basename = file_name.dropRight(
if file_name.endsWith(".hyt") then ".hyt".length if file_name.endsWith(".hyt") then ".hyt".length
else ".hytrans".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\")." logger `warn` s"translation file \"$file_name\" is not in language index, so it got ignored (normalized lang name is \"$normalized\")."
break() break()
val lang_def = try { val lang_def = try {
val content = file.readAsString() val content = file.readString
Parser.parse(content) Parser.parse(content)
} catch case e: IOException => } catch case e: IOException =>
logger `error` logger `error`

View File

@ -1,7 +1,8 @@
package cc.sukazyo.cono.morny.core package cc.sukazyo.cono.morny.core
import cc.sukazyo.cono.morny.core.Log.logger 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 cc.sukazyo.cono.morny.util.CommonFormat
import java.time.ZoneOffset import java.time.ZoneOffset
@ -19,6 +20,7 @@ object ServerMain {
val config = new MornyConfig.Prototype() val config = new MornyConfig.Prototype()
var mode_echoVersion = false var mode_echoVersion = false
var mode_echoModules = false
var mode_echoHello = false var mode_echoHello = false
var mode_testRun = false var mode_testRun = false
var showHello = true var showHello = true
@ -45,6 +47,7 @@ object ServerMain {
case "--no-hello" | "-hf" | "--quiet" | "-q" => showHello = false case "--no-hello" | "-hf" | "--quiet" | "-q" => showHello = false
case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true
case "--version" | "-v" => mode_echoVersion = true case "--version" | "-v" => mode_echoVersion = true
case "--modules" | "-mod" => mode_echoModules = true
// deprecated: use --outdated-ignore instead // deprecated: use --outdated-ignore instead
// case "--outdated-block" | "-ob" => // case "--outdated-block" | "-ob" =>
@ -167,6 +170,8 @@ object ServerMain {
s"""The Skip Login feature is not implemented yet! s"""The Skip Login feature is not implemented yet!
|""".stripMargin |""".stripMargin
val loadedModules = ModuleLoader.loadCoreModules()
if (mode_echoVersion) { if (mode_echoVersion) {
logger `info` logger `info`
@ -195,10 +200,18 @@ object ServerMain {
| ${MornySystem.CODE_TIMESTAMP} | ${MornySystem.CODE_TIMESTAMP}
| ${CommonFormat.formatDate(MornySystem.CODE_TIMESTAMP, 0)} [UTC]""" | ${CommonFormat.formatDate(MornySystem.CODE_TIMESTAMP, 0)} [UTC]"""
.stripMargin .stripMargin
return
} }
if (mode_echoModules) {
logger `info` s"Loaded modules ::: \n${ModuleHelper.drawTable(loadedModules)}"
}
if mode_echoVersion | mode_echoModules then
return
logger `info` logger `info`
s"""ServerMain.java Loaded >>> s"""ServerMain.java Loaded >>>
|- version ${MornySystem.VERSION_FULL} |- version ${MornySystem.VERSION_FULL}
@ -222,7 +235,7 @@ object ServerMain {
try try
MornyCoeur( MornyCoeur(
ServerModulesLoader.load() loadedModules
)(using config build)( )(using config build)(
testRun = mode_testRun testRun = mode_testRun
) )

View File

@ -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(),
)
}
}

View File

@ -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) *
)
}
}

View File

@ -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
}
}

View File

@ -0,0 +1,9 @@
package cc.sukazyo.cono.morny.core.module
object ModulesJarLoader {
def load (): Unit = {
// TODO
}
}

View File

@ -1,11 +1,14 @@
package cc.sukazyo.cono.morny.data package cc.sukazyo.cono.morny.data
import cc.sukazyo.restools.ResourcesPackage import cc.sukazyo.restools.{ResourceDirectory, ResourcePackage}
object MornyAssets { object MornyAssets {
class AssetsException (caused: Throwable) extends Exception("Cannot read assets file.", caused) 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*)
} }

View File

@ -37,7 +37,7 @@ object MornyInformation {
catch case _: UnknownHostException => None catch case _: UnknownHostException => None
} }
def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT.get
def getMornyAboutLinksVars: List[Var] = def getMornyAboutLinksVars: List[Var] =
List( List(

View File

@ -8,7 +8,7 @@ import scala.util.Using
object TelegramImages { object TelegramImages {
class AssetsFileImage (assetsPath: String) { class AssetsFileImage (assetsPath: List[String]) {
private var cache: Option[Array[Byte]] = None private var cache: Option[Array[Byte]] = None
@ -19,7 +19,7 @@ object TelegramImages {
@throws[AssetsException] @throws[AssetsException]
private def read (): Unit = { private def read (): Unit = {
Using ((MornyAssets.pack `getResource` assetsPath)read) { stream => Using (MornyAssets.assets.getFile(assetsPath*).read()) { stream =>
try { this.cache = Some(stream.readAllBytes()) } try { this.cache = Some(stream.readAllBytes()) }
catch case e: IOException => { catch case e: IOException => {
throw AssetsException(e) throw AssetsException(e)
@ -29,11 +29,18 @@ object TelegramImages {
} }
val IMG_ABOUT: AssetsFileImage = AssetsFileImage("images/featured-image@0.5x.jpg") object AssetsFileImage {
val IMG_400: AssetsFileImage = AssetsFileImage("images/http-sekai-400.png") def byId (id: String): AssetsFileImage =
val IMG_404: AssetsFileImage = AssetsFileImage("images/http-sekai-404.png") byId(id, "png")
val IMG_500: AssetsFileImage = AssetsFileImage("images/http-sekai-500.png") def byId (id: String, ty: String): AssetsFileImage =
val IMG_501: AssetsFileImage = AssetsFileImage("images/http-sekai-501.png") AssetsFileImage("images" :: s"$id.$ty" :: Nil);
val IMG_523: AssetsFileImage = AssetsFileImage("images/http-sekai-523.png") }
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")
} }

View File

@ -1,13 +1,13 @@
package cc.sukazyo.cono.morny.test 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.freespec.AnyFreeSpec
import org.scalatest.matchers.should import org.scalatest.matchers.should
abstract class MornyTests extends AnyFreeSpec with should.Matchers { abstract class MornyTests extends AnyFreeSpec with should.Matchers {
val assets: ResourcesPackage = val pack: ResourcePackage = ResourcePackage.get("assets_morny_tests")
ResourcesPackage(classOf[MornyTests], "assets_morny_tests") val assets: ResourceDirectory = pack.getDirectory("assets_morny_tests")
val pending_val = "[not-implemented]" val pending_val = "[not-implemented]"

View File

@ -83,7 +83,7 @@ class CommonEncryptTest extends MornyTests with TableDrivenPropertyChecks {
if file == null then if file == null then
Array.empty[Byte] Array.empty[Byte]
else else
assets.getResource(file).read.readAllBytes assets.getFile(file).read.readAllBytes
s"while hashing binary $_name :" - { s"while hashing binary $_name :" - {