mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-24 12:07:39 +08:00
Compare commits
3 Commits
7e3588c221
...
f2bd0a38c2
Author | SHA1 | Date | |
---|---|---|---|
f2bd0a38c2 | |||
f3db8f4457 | |||
2cbc75a2ca |
@ -3,7 +3,7 @@ aether.AetherKeys.aetherOldVersionMethod := true
|
|||||||
ThisBuild / organization := "cc.sukazyo"
|
ThisBuild / organization := "cc.sukazyo"
|
||||||
ThisBuild / organizationName := "A.C. Sukazyo Eyre"
|
ThisBuild / organizationName := "A.C. Sukazyo Eyre"
|
||||||
|
|
||||||
ThisBuild / scalaVersion := "3.4.0-RC4"
|
ThisBuild / scalaVersion := "3.4.1"
|
||||||
|
|
||||||
resolvers ++= Seq(
|
resolvers ++= Seq(
|
||||||
"-ws-releases" at "https://mvn.sukazyo.cc/releases"
|
"-ws-releases" at "https://mvn.sukazyo.cc/releases"
|
||||||
|
@ -16,18 +16,18 @@ object MornyConfiguration {
|
|||||||
|
|
||||||
val dependencies: Seq[ModuleID] = Seq(
|
val dependencies: Seq[ModuleID] = Seq(
|
||||||
|
|
||||||
"com.github.spotbugs" % "spotbugs-annotations" % "4.8.2" % 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.2.2",
|
||||||
|
|
||||||
"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.25",
|
"org.http4s" %% "http4s-dsl" % "0.23.26",
|
||||||
"org.http4s" %% "http4s-circe" % "0.23.25",
|
"org.http4s" %% "http4s-circe" % "0.23.26",
|
||||||
"org.http4s" %% "http4s-netty-server" % "0.5.12",
|
"org.http4s" %% "http4s-netty-server" % "0.5.16",
|
||||||
|
|
||||||
"com.softwaremill.sttp.client3" %% "core" % "3.9.2",
|
"com.softwaremill.sttp.client3" %% "core" % "3.9.5",
|
||||||
"com.softwaremill.sttp.client3" %% "okhttp-backend" % "3.9.2",
|
"com.softwaremill.sttp.client3" %% "okhttp-backend" % "3.9.5",
|
||||||
"com.squareup.okhttp3" % "okhttp" % "4.12.0" % Runtime,
|
"com.squareup.okhttp3" % "okhttp" % "4.12.0" % Runtime,
|
||||||
|
|
||||||
"org.typelevel" %% "case-insensitive" % "1.4.0",
|
"org.typelevel" %% "case-insensitive" % "1.4.0",
|
||||||
@ -42,10 +42,10 @@ object MornyConfiguration {
|
|||||||
// used for disable slf4j
|
// used for disable slf4j
|
||||||
// due to the slf4j api have been used in the following libraries:
|
// due to the slf4j api have been used in the following libraries:
|
||||||
// - cron-utils
|
// - cron-utils
|
||||||
"org.slf4j" % "slf4j-nop" % "2.0.9" % Runtime,
|
"org.slf4j" % "slf4j-nop" % "2.0.13" % Runtime,
|
||||||
|
|
||||||
"org.scalatest" %% "scalatest" % "3.2.17" % Test,
|
"org.scalatest" %% "scalatest" % "3.2.18" % Test,
|
||||||
"org.scalatest" %% "scalatest-freespec" % "3.2.17" % Test,
|
"org.scalatest" %% "scalatest-freespec" % "3.2.18" % Test,
|
||||||
// for test report
|
// for test report
|
||||||
"com.vladsch.flexmark" % "flexmark" % "0.64.8" % Test,
|
"com.vladsch.flexmark" % "flexmark" % "0.64.8" % Test,
|
||||||
"com.vladsch.flexmark" % "flexmark-profile-pegdown" % "0.64.8" % Test
|
"com.vladsch.flexmark" % "flexmark-profile-pegdown" % "0.64.8" % Test
|
||||||
|
@ -1 +1 @@
|
|||||||
sbt.version=1.9.4
|
sbt.version=1.9.9
|
||||||
|
BIN
src/main/resources/assets_morny/images/http-sekai-400.png
Normal file
BIN
src/main/resources/assets_morny/images/http-sekai-400.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 105 KiB |
30
src/main/resources/assets_morny/langs/_index.hyl
Normal file
30
src/main/resources/assets_morny/langs/_index.hyl
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
en, 2 000 000 000
|
||||||
|
en_us, 328 000 000
|
||||||
|
en_gb, 66 300 000
|
||||||
|
en_sg, 5 000 000
|
||||||
|
en_mt, 1
|
||||||
|
en_ph, 100 000 000
|
||||||
|
en_nz, 5 000 000
|
||||||
|
en_za, 5 000 000
|
||||||
|
en_au, 25 000 000
|
||||||
|
en_ie, 5 000 000
|
||||||
|
en_ca, 25 000 000
|
||||||
|
en_in, 125 000 000
|
||||||
|
zh, 1 000 000 000
|
||||||
|
zh_hans, 1 400 000 000
|
||||||
|
zh_cn, 1 300 000 000
|
||||||
|
zh_sg, 5 000 000
|
||||||
|
zh_mo, 2 000 000
|
||||||
|
zh_hant, 50 000 000
|
||||||
|
zh_tw, 23 000 000
|
||||||
|
zh_hk, 7 000 000
|
||||||
|
zh_ancient, 10
|
||||||
|
ja, 125 000 000
|
||||||
|
ja_jp, 10 000 000
|
||||||
|
fr, 338 000 000
|
||||||
|
fr_fr, 67 000 000
|
||||||
|
fr_ch, 8 000 000
|
||||||
|
fr_be, 11 000 000
|
||||||
|
fr_ca, 7 000 000
|
||||||
|
fr_lu, 1
|
||||||
|
mt_mt, 520 000
|
53
src/main/resources/assets_morny/langs/en_us.hyt
Normal file
53
src/main/resources/assets_morny/langs/en_us.hyt
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
Morny Translations File
|
||||||
|
|
||||||
|
%1.0
|
||||||
|
&encoding=utf8
|
||||||
|
&indent=1
|
||||||
|
|
||||||
|
morny.command.info.message.about
|
||||||
|
| <b>Morny Cono</b>
|
||||||
|
| An assistant rat(micromys minutus) from Annie。
|
||||||
|
| ————————————————
|
||||||
|
| {about_links}
|
||||||
|
|
||||||
|
morny.command.info.version
|
||||||
|
| version:
|
||||||
|
| - Morny <code>{version_codename}</code>
|
||||||
|
| - <code>{version_base}</code>{version_delta_html}{version_git_suffix}
|
||||||
|
| coeur md5_hash:
|
||||||
|
| - <code>{md5}</code>
|
||||||
|
| coding timestamp:
|
||||||
|
| - <code>{time_millis}</code>
|
||||||
|
| - <code>{time_utc} [UTC]</code>
|
||||||
|
|
||||||
|
morny.command.info.message.runtime
|
||||||
|
| system:
|
||||||
|
| - <code>{hostname}</code>
|
||||||
|
| - <code>{os.name}</code> <code>{os.arch}</code> <code>{os.version}</code>
|
||||||
|
| java runtime:
|
||||||
|
| - <code>{java.vm.vendor}.{java.vm.name}</code>
|
||||||
|
| - <code>{java.vm.version}</code>
|
||||||
|
| vm memory:
|
||||||
|
| - <code>{memory_used_mb}</code> / <code>{memory_available_mb}</code> MB
|
||||||
|
| - <code>{cpu_cores}</code> cores
|
||||||
|
| coeur version:
|
||||||
|
| - {version_full}
|
||||||
|
| - <code>{coeur_md5}</code>
|
||||||
|
| - <code>{compile_time_utc} [UTC]</code>
|
||||||
|
| - [<code>{compile_time_millis}</code>]
|
||||||
|
| continuous:
|
||||||
|
| - <code>{running_duration}</code>
|
||||||
|
| - [<code>{running_duration_ms}</code>]
|
||||||
|
| - <code>{startup_time_utc}</code>
|
||||||
|
| - [<code>{startup_time_millis}</code>]
|
||||||
|
|
||||||
|
morny.command.info.message.tasks
|
||||||
|
| <b>Coeur Task Scheduler:</b>
|
||||||
|
| - <i>scheduled tasks</i>: <code>{coeur.tasks.amount}</code>
|
||||||
|
| - <i>scheduler status</i>: <code>{coeur.tasks.state}</code>
|
||||||
|
| - <i>current runner status</i>: <code>{coeur.tasks.runnerState}</code>
|
||||||
|
|
||||||
|
morny.command.info.message.event
|
||||||
|
| <b>Event Statistics :</b>
|
||||||
|
| in today
|
||||||
|
| {event_statistics}""".stripMargin
|
11
src/main/resources/assets_morny/langs/zh_cn.hyt
Normal file
11
src/main/resources/assets_morny/langs/zh_cn.hyt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Morny Translations File
|
||||||
|
|
||||||
|
%1.0
|
||||||
|
&encoding=utf8
|
||||||
|
&indent=1
|
||||||
|
|
||||||
|
morny.command.info.about
|
||||||
|
| <b>Morny Cono</b>
|
||||||
|
| 来自安妮的侍从小鼠。
|
||||||
|
| ————————————————
|
||||||
|
| {about_links}
|
@ -1,6 +1,6 @@
|
|||||||
package cc.sukazyo.cono.morny.core
|
package cc.sukazyo.cono.morny.core
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.core.MornyAssets
|
import cc.sukazyo.cono.morny.data.MornyAssets
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
|
@ -397,7 +397,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
|||||||
break(Some(LoginResult(account, remote.username, remote.id)))
|
break(Some(LoginResult(account, remote.username, remote.id)))
|
||||||
} catch
|
} catch
|
||||||
case r: boundary.Break[Option[LoginResult]] => throw r
|
case r: boundary.Break[Option[LoginResult]] => throw r
|
||||||
case e =>
|
case e: Throwable =>
|
||||||
logger `error`
|
logger `error`
|
||||||
s"""${e.toLogString}
|
s"""${e.toLogString}
|
||||||
|login failed"""
|
|login failed"""
|
||||||
|
@ -2,17 +2,14 @@ package cc.sukazyo.cono.morny.core.http
|
|||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
||||||
import cc.sukazyo.cono.morny.data.TelegramImages
|
import org.http4s.HttpRoutes
|
||||||
import org.http4s.{HttpRoutes, MediaType}
|
|
||||||
import org.http4s.dsl.io.*
|
import org.http4s.dsl.io.*
|
||||||
import org.http4s.headers.`Content-Type`
|
|
||||||
|
|
||||||
class ServiceUI extends HttpService4Api {
|
class ServiceUI extends HttpService4Api {
|
||||||
|
|
||||||
override lazy val service: HttpRoutes[IO] = HttpRoutes.of[IO] {
|
override lazy val service: HttpRoutes[IO] = HttpRoutes.of[IO] {
|
||||||
case GET -> Root =>
|
case GET -> Root =>
|
||||||
NotImplemented(TelegramImages.IMG_501.get)
|
MornyNotImplemented()
|
||||||
.map(_.withContentType(`Content-Type`(MediaType.image.jpeg)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,12 @@
|
|||||||
package cc.sukazyo.cono.morny.core.http.api
|
package cc.sukazyo.cono.morny.core.http.api
|
||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
import org.http4s.HttpRoutes
|
||||||
import org.http4s.{HttpRoutes, Response}
|
|
||||||
|
|
||||||
trait HttpService4Api {
|
trait HttpService4Api extends HttpStatus {
|
||||||
|
|
||||||
lazy val service: HttpRoutes[IO]
|
lazy val service: HttpRoutes[IO]
|
||||||
|
|
||||||
extension (response: Response[IO]) {
|
|
||||||
def setMornyInternalErrorHeader (e: Throwable): Response[IO] =
|
|
||||||
response.setMornyInternalErrorHeader(
|
|
||||||
e.getClass.getSimpleName,
|
|
||||||
e.getMessage,
|
|
||||||
e.toLogString
|
|
||||||
)
|
|
||||||
def setMornyInternalErrorHeader (
|
|
||||||
`Morny-Internal-Error-Type`: String,
|
|
||||||
`Morny-Internal-Error-Message`: String,
|
|
||||||
`Morny-Internal-Error-Detail`: String
|
|
||||||
): Response[IO] =
|
|
||||||
response.withHeaders(
|
|
||||||
"Morny-Internal-Error-Type" -> `Morny-Internal-Error-Type`,
|
|
||||||
"Morny-Internal-Error-Message" -> `Morny-Internal-Error-Message`,
|
|
||||||
"Morny-Internal-Error-Detail" -> `Morny-Internal-Error-Detail`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object HttpService4Api {
|
object HttpService4Api {
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
package cc.sukazyo.cono.morny.core.http.api
|
||||||
|
|
||||||
|
import cats.effect.IO
|
||||||
|
import cc.sukazyo.cono.morny.data.TelegramImages
|
||||||
|
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||||
|
import org.http4s.{Header, MediaType, Response}
|
||||||
|
import org.http4s.dsl.io.*
|
||||||
|
import org.http4s.headers.`Content-Type`
|
||||||
|
|
||||||
|
trait HttpStatus {
|
||||||
|
|
||||||
|
private type ResponseT = Response[IO]
|
||||||
|
private type ResponseIO = IO[ResponseT]
|
||||||
|
|
||||||
|
extension (response: ResponseT) {
|
||||||
|
def setMornyInternalErrorHeader (e: Throwable): ResponseT =
|
||||||
|
response.setMornyInternalErrorHeader(
|
||||||
|
e.getClass.getSimpleName,
|
||||||
|
e.getMessage,
|
||||||
|
e.toLogString
|
||||||
|
)
|
||||||
|
def setMornyInternalErrorHeader (
|
||||||
|
`Morny-Internal-Error-Type`: String,
|
||||||
|
`Morny-Internal-Error-Message`: String,
|
||||||
|
`Morny-Internal-Error-Detail`: String
|
||||||
|
): ResponseT =
|
||||||
|
response
|
||||||
|
.putHeaders(
|
||||||
|
"Morny-Internal-Error-Type" -> `Morny-Internal-Error-Type`,
|
||||||
|
"Morny-Internal-Error-Message" -> `Morny-Internal-Error-Message`,
|
||||||
|
"Morny-Internal-Error-Detail" -> `Morny-Internal-Error-Detail`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 400 Bad Request */
|
||||||
|
def MornyBadRequest (): ResponseIO =
|
||||||
|
BadRequest(TelegramImages.IMG_400.get)
|
||||||
|
.map(_.withContentType(`Content-Type`(MediaType.image.png)))
|
||||||
|
|
||||||
|
/** 404 Not Found */
|
||||||
|
def MornyNotFound (): ResponseIO =
|
||||||
|
NotFound(TelegramImages.IMG_404.get)
|
||||||
|
.map(_.withContentType(`Content-Type`(MediaType.image.png)))
|
||||||
|
|
||||||
|
/** 500 Internal Server Error */
|
||||||
|
def MornyInternalServerError (): ResponseIO =
|
||||||
|
InternalServerError(TelegramImages.IMG_500.get)
|
||||||
|
.map(_.withContentType(`Content-Type`(MediaType.image.png)))
|
||||||
|
|
||||||
|
/** 501 Not Implemented */
|
||||||
|
def MornyNotImplemented (): ResponseIO =
|
||||||
|
NotImplemented(TelegramImages.IMG_501.get)
|
||||||
|
.map(_.withContentType(`Content-Type`(MediaType.image.png)))
|
||||||
|
|
||||||
|
/** 523 Service Unavailable */
|
||||||
|
def MornyServiceUnavailable (): ResponseIO =
|
||||||
|
ServiceUnavailable(TelegramImages.IMG_523.get)
|
||||||
|
.map(_.withContentType(`Content-Type`(MediaType.image.png)))
|
||||||
|
|
||||||
|
}
|
@ -2,14 +2,18 @@ package cc.sukazyo.cono.morny.core.http.services
|
|||||||
|
|
||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
||||||
|
import cc.sukazyo.cono.morny.core.MornySystem
|
||||||
|
import cc.sukazyo.cono.morny.util.CommonFormat
|
||||||
import org.http4s.{HttpRoutes, Response}
|
import org.http4s.{HttpRoutes, Response}
|
||||||
import org.http4s.circe.jsonEncoder
|
import org.http4s.circe.jsonEncoder
|
||||||
import org.http4s.dsl.io.*
|
import org.http4s.dsl.io.*
|
||||||
|
|
||||||
class Ping extends HttpService4Api {
|
class Ping extends HttpService4Api {
|
||||||
|
|
||||||
case class PingResult (
|
private case class PingResult (
|
||||||
pong: Boolean = true
|
pong: Boolean = true,
|
||||||
|
time: String = CommonFormat.formatDate(System.currentTimeMillis(), 0),
|
||||||
|
server: String = MornySystem.VERSION_FULL,
|
||||||
)
|
)
|
||||||
|
|
||||||
override lazy val service: HttpRoutes[IO] = HttpRoutes.of[IO] {
|
override lazy val service: HttpRoutes[IO] = HttpRoutes.of[IO] {
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package cc.sukazyo.cono.morny.core
|
package cc.sukazyo.cono.morny.data
|
||||||
|
|
||||||
import cc.sukazyo.restools.ResourcesPackage
|
import cc.sukazyo.restools.ResourcesPackage
|
||||||
|
|
@ -1,7 +1,6 @@
|
|||||||
package cc.sukazyo.cono.morny.data
|
package cc.sukazyo.cono.morny.data
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.core.MornyAssets
|
import cc.sukazyo.cono.morny.data.MornyAssets.AssetsException
|
||||||
import cc.sukazyo.cono.morny.core.MornyAssets.AssetsException
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import scala.language.postfixOps
|
import scala.language.postfixOps
|
||||||
@ -31,6 +30,7 @@ object TelegramImages {
|
|||||||
}
|
}
|
||||||
|
|
||||||
val IMG_ABOUT: AssetsFileImage = AssetsFileImage("images/featured-image@0.5x.jpg")
|
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_404: AssetsFileImage = AssetsFileImage("images/http-sekai-404.png")
|
||||||
val IMG_500: AssetsFileImage = AssetsFileImage("images/http-sekai-500.png")
|
val IMG_500: AssetsFileImage = AssetsFileImage("images/http-sekai-500.png")
|
||||||
val IMG_501: AssetsFileImage = AssetsFileImage("images/http-sekai-501.png")
|
val IMG_501: AssetsFileImage = AssetsFileImage("images/http-sekai-501.png")
|
||||||
|
@ -3,14 +3,12 @@ package cc.sukazyo.cono.morny.stickers_get.http
|
|||||||
import cats.effect.IO
|
import cats.effect.IO
|
||||||
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
||||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||||
import cc.sukazyo.cono.morny.data.TelegramImages
|
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute
|
||||||
import com.pengrad.telegrambot.request.GetFile
|
import com.pengrad.telegrambot.request.GetFile
|
||||||
import com.pengrad.telegrambot.TelegramBot
|
import com.pengrad.telegrambot.TelegramBot
|
||||||
import org.http4s.{HttpRoutes, MediaType}
|
import org.http4s.HttpRoutes
|
||||||
import org.http4s.dsl.io.*
|
import org.http4s.dsl.io.*
|
||||||
import org.http4s.headers.`Content-Type`
|
|
||||||
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
|
|
||||||
@ -28,34 +26,26 @@ class StickerService (using coeur: MornyCoeur) extends HttpService4Api {
|
|||||||
Ok(file)
|
Ok(file)
|
||||||
} catch {
|
} catch {
|
||||||
case e: IOException =>
|
case e: IOException =>
|
||||||
ServiceUnavailable(
|
MornyServiceUnavailable()
|
||||||
TelegramImages.IMG_523.get,
|
.map(_.setMornyInternalErrorHeader(e))
|
||||||
`Content-Type`(MediaType.image.png),
|
|
||||||
).map(_.setMornyInternalErrorHeader(e))
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
NotFound(
|
MornyNotFound()
|
||||||
TelegramImages.IMG_404.get,
|
.map(_.setMornyInternalErrorHeader(
|
||||||
`Content-Type`(MediaType.image.png),
|
|
||||||
).map(_.setMornyInternalErrorHeader(
|
|
||||||
"_telegram_api",
|
"_telegram_api",
|
||||||
response.errorCode.toString,
|
response.errorCode.toString,
|
||||||
response.description,
|
response.description,
|
||||||
))
|
))
|
||||||
} catch
|
} catch
|
||||||
case io: IOException =>
|
case io: IOException =>
|
||||||
ServiceUnavailable(
|
MornyServiceUnavailable()
|
||||||
TelegramImages.IMG_523.get,
|
.map(_.setMornyInternalErrorHeader(io))
|
||||||
`Content-Type`(MediaType.image.png),
|
|
||||||
).map(_.setMornyInternalErrorHeader(io))
|
|
||||||
case e: Throwable =>
|
case e: Throwable =>
|
||||||
InternalServerError(
|
MornyInternalServerError()
|
||||||
TelegramImages.IMG_500.get,
|
.map(_.setMornyInternalErrorHeader(e))
|
||||||
`Content-Type`(MediaType.image.png),
|
|
||||||
).map(_.setMornyInternalErrorHeader(e))
|
|
||||||
|
|
||||||
case GET -> Root / "sticker" =>
|
case GET -> "sticker" /: rest =>
|
||||||
NotFound("not found")
|
MornyBadRequest() // TODO: bad request
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,14 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util.hytrans
|
||||||
|
|
||||||
|
//opaque type Definitions = Map[String, String]
|
||||||
|
|
||||||
|
class Definitions (
|
||||||
|
innerData: Map[String, String]
|
||||||
|
) extends Map[String, String] {
|
||||||
|
|
||||||
|
def iterator: Iterator[(String, String)] = innerData.iterator
|
||||||
|
def removed (key: String): Map[String, String] = innerData.removed(key)
|
||||||
|
def updated[V1 >: String] (key: String, value: V1): Map[String, V1] = innerData.updated(key, value)
|
||||||
|
def get (key: String): Option[String] = innerData.get(key)
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util.hytrans
|
||||||
|
|
||||||
|
case class LangTag (
|
||||||
|
lang: String,
|
||||||
|
priority: Long
|
||||||
|
) extends Comparable[LangTag] {
|
||||||
|
|
||||||
|
override def compareTo(o: LangTag): Int =
|
||||||
|
this.priority `compareTo` o.priority
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object LangTag {
|
||||||
|
|
||||||
|
class IllegalLangTagException (message: String, val original: String)
|
||||||
|
extends IllegalArgumentException(message)
|
||||||
|
|
||||||
|
def normalizeLangTag(tag: String): String =
|
||||||
|
tag.replaceAll("-", "_").toLowerCase
|
||||||
|
|
||||||
|
@throws[IllegalLangTagException]
|
||||||
|
def ensureLangTag (tag: String): String =
|
||||||
|
tag.foreach {
|
||||||
|
case c if c.isLetter =>
|
||||||
|
case '_' =>
|
||||||
|
case ' ' | '\t' | '\r' | '\n' =>
|
||||||
|
throw IllegalLangTagException("Lang Tag cannot contains space", tag)
|
||||||
|
case ill =>
|
||||||
|
throw IllegalLangTagException(s"Illegal character '$ill' in Lang Tag \"$tag\"", tag)
|
||||||
|
}
|
||||||
|
tag
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,188 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util.hytrans
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.util.hytrans.LangTag.IllegalLangTagException
|
||||||
|
|
||||||
|
import java.io.{PrintWriter, StringWriter}
|
||||||
|
import scala.collection.mutable
|
||||||
|
import scala.util.boundary
|
||||||
|
|
||||||
|
class LanguageTree {
|
||||||
|
|
||||||
|
class Node (val langTag: LangTag) extends Comparable[Node] {
|
||||||
|
private object LangTagOrdering extends Ordering[Node]:
|
||||||
|
override def compare (x: Node, y: Node): Int = y.langTag `compareTo` x.langTag
|
||||||
|
|
||||||
|
override def compareTo (o: Node): Int = this.langTag `compareTo` o.langTag
|
||||||
|
|
||||||
|
private type ChildCol = mutable.SortedSet[Node]
|
||||||
|
|
||||||
|
private var _parent: Option[Node] = None
|
||||||
|
private val _children: ChildCol = mutable.SortedSet.empty(using LangTagOrdering)
|
||||||
|
|
||||||
|
def parent: Option[Node] = _parent
|
||||||
|
def children: ChildCol = _children
|
||||||
|
|
||||||
|
def traverseParent (f: Node => Unit): Unit =
|
||||||
|
_parent.foreach { parent =>
|
||||||
|
f(parent)
|
||||||
|
parent.traverseParent(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
private def traversingTree (f: Node => Unit)(using visited: mutable.Set[Node]): Unit =
|
||||||
|
_children.foreach { child =>
|
||||||
|
if !(visited contains child) then
|
||||||
|
child.traversingTree(f)
|
||||||
|
}
|
||||||
|
if !(visited contains this) then
|
||||||
|
f(this)
|
||||||
|
visited += this
|
||||||
|
this.parent.foreach { parent =>
|
||||||
|
if !(visited contains parent) then
|
||||||
|
parent.traversingTree(f)
|
||||||
|
}
|
||||||
|
|
||||||
|
def traverseTree (f: Node=>Unit): Unit =
|
||||||
|
val visited = mutable.Set.empty[Node]
|
||||||
|
f(this)
|
||||||
|
visited += this
|
||||||
|
traversingTree(f)(using visited)
|
||||||
|
|
||||||
|
@throws[IllegalArgumentException]
|
||||||
|
private infix def setParent (parent: Node): Unit =
|
||||||
|
this._parent = Some(parent)
|
||||||
|
parent.traverseParent { p =>
|
||||||
|
if p == parent then
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"failed set parent ${parent.langTag.lang} to ${langTag.lang}: " +
|
||||||
|
s"Cannot set parent to a child of itself"
|
||||||
|
)
|
||||||
|
this._parent = None
|
||||||
|
}
|
||||||
|
|
||||||
|
def detachParent (): Unit =
|
||||||
|
this._parent.foreach(_.removeChild(this))
|
||||||
|
|
||||||
|
def removeChild (child: Node): Unit =
|
||||||
|
child._parent = None
|
||||||
|
_children -= child
|
||||||
|
|
||||||
|
@throws[IllegalArgumentException]
|
||||||
|
infix def addChild (child: Node): Unit =
|
||||||
|
val child_old_parent = child._parent
|
||||||
|
if child._parent.nonEmpty then
|
||||||
|
child.detachParent()
|
||||||
|
try child setParent this
|
||||||
|
catch case e: IllegalArgumentException =>
|
||||||
|
child_old_parent.foreach(_ addChild child)
|
||||||
|
throw e
|
||||||
|
this._children += child
|
||||||
|
|
||||||
|
@throws[IllegalArgumentException]
|
||||||
|
infix def addChild (child: LangTag): Node =
|
||||||
|
val node = new Node(child)
|
||||||
|
addChild(node)
|
||||||
|
node
|
||||||
|
|
||||||
|
private def printTree (node: Node, prefix: String = "", printer: PrintWriter): Unit = {
|
||||||
|
printer.println(s"$prefix${node.langTag}")
|
||||||
|
node.children.foreach { child =>
|
||||||
|
printTree(child, prefix + " ", printer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def printTree: String =
|
||||||
|
val s = StringWriter()
|
||||||
|
printTree(this, "", PrintWriter(s))
|
||||||
|
s.toString
|
||||||
|
|
||||||
|
override def toString: String =
|
||||||
|
printTree
|
||||||
|
|
||||||
|
}
|
||||||
|
object Node:
|
||||||
|
def defaultRoot: Node = Node(LangTag("root", 0))
|
||||||
|
|
||||||
|
val root: Node = Node.defaultRoot
|
||||||
|
|
||||||
|
def search (langTag: String): Option[Node] =
|
||||||
|
val _langTag = LangTag.normalizeLangTag(langTag)
|
||||||
|
boundary {
|
||||||
|
root.traverseTree { node =>
|
||||||
|
if node.langTag.lang == _langTag then
|
||||||
|
boundary.break(Some(node))
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object LanguageTree {
|
||||||
|
|
||||||
|
@throws[IllegalArgumentException]
|
||||||
|
def parseTreeDocument (document: String): LanguageTree = {
|
||||||
|
|
||||||
|
val lines = document.replaceAll("\\r", "").split('\n')
|
||||||
|
val tree = new LanguageTree
|
||||||
|
val root = tree.root
|
||||||
|
import tree.Node
|
||||||
|
var currentLevel = mutable.ListBuffer[Node](root)
|
||||||
|
|
||||||
|
def countHeadingWhitespaceLevel (line: String)(using whitespaceSize: Int): Option[(Int, String)] =
|
||||||
|
val whitespace = line.takeWhile(_.isWhitespace)
|
||||||
|
if whitespace.length % whitespaceSize == 0 then
|
||||||
|
Some(whitespace.length / whitespaceSize -> line.drop(whitespace.length))
|
||||||
|
else None
|
||||||
|
|
||||||
|
for (i <- lines.indices) {
|
||||||
|
val line = lines(i)
|
||||||
|
val line_number = i+1
|
||||||
|
countHeadingWhitespaceLevel(line)(using 2) match
|
||||||
|
case Some((level, content)) =>
|
||||||
|
val langTag: LangTag = content.split(",", 2) match
|
||||||
|
case Array(lang) =>
|
||||||
|
try LangTag(LangTag.ensureLangTag(LangTag.normalizeLangTag(lang)), 0)
|
||||||
|
catch case e: IllegalLangTagException =>
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"illegal lang name at line $line_number: ${e.getMessage}"
|
||||||
|
).initCause(e)
|
||||||
|
case Array(lang, priority) =>
|
||||||
|
try LangTag(
|
||||||
|
LangTag.ensureLangTag(LangTag.normalizeLangTag(lang)),
|
||||||
|
priority.filterNot(List(' ', ',', '-', '_', '\'').contains(_)).toInt
|
||||||
|
)
|
||||||
|
catch
|
||||||
|
case e: NumberFormatException =>
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"failed parse lang's priority at line $line_number: ${e.getMessage}"
|
||||||
|
).initCause(e)
|
||||||
|
case e: IllegalLangTagException =>
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"illegal lang name at line $line_number: ${e.getMessage}"
|
||||||
|
).initCause(e)
|
||||||
|
case _ =>
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"failed parse at line $line_number: line with invalid format."
|
||||||
|
)
|
||||||
|
val node = Node(langTag)
|
||||||
|
if level < (currentLevel.length + 1) then
|
||||||
|
currentLevel = currentLevel take (level + 1)
|
||||||
|
currentLevel.last addChild node
|
||||||
|
currentLevel += node
|
||||||
|
else if level == (currentLevel.length + 1) then
|
||||||
|
currentLevel.last addChild node
|
||||||
|
currentLevel += node
|
||||||
|
else
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"failed parse at line $line_number: line with invalid indentation."
|
||||||
|
)
|
||||||
|
case None =>
|
||||||
|
throw new IllegalArgumentException(
|
||||||
|
s"failed parse at line $line_number: line with invalid indentation."
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
tree
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,41 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util.hytrans
|
||||||
|
|
||||||
|
object Parser {
|
||||||
|
|
||||||
|
def parse (document: String): Definitions = {
|
||||||
|
|
||||||
|
val lines = document.replaceAll("\\r", "").split('\n')
|
||||||
|
|
||||||
|
val keyValues = collection.mutable.Map.empty[String, String]
|
||||||
|
var keyDef: String | Null = null
|
||||||
|
def newValue = StringBuilder()
|
||||||
|
var valueDef: StringBuilder = newValue
|
||||||
|
def addLine (line: String) =
|
||||||
|
valueDef ++= line += '\n'
|
||||||
|
//noinspection TypeAnnotation
|
||||||
|
def saveThis() =
|
||||||
|
if keyDef != null then
|
||||||
|
keyValues += (keyDef -> valueDef.toString.stripSuffix("\n"))
|
||||||
|
keyDef = null
|
||||||
|
valueDef = newValue
|
||||||
|
|
||||||
|
lines.foreach { line =>
|
||||||
|
line.headOption match {
|
||||||
|
case Some(' ') | Some('\t') | None => // empty lines, will be ignored
|
||||||
|
case Some('#') => // comment line, will be ignored
|
||||||
|
case Some('%') => // document meta definition line, currently not supported
|
||||||
|
case Some('|') => // content line
|
||||||
|
addLine(line drop 2)
|
||||||
|
case Some(_) => // a key definition line
|
||||||
|
saveThis()
|
||||||
|
keyDef = line
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
saveThis()
|
||||||
|
|
||||||
|
Definitions(keyValues.toMap)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util.hytrans
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.util.var_text.{Var, VarText}
|
||||||
|
|
||||||
|
import scala.util.boundary
|
||||||
|
|
||||||
|
class Translations (
|
||||||
|
langs: LanguageTree,
|
||||||
|
translations: Map[String, Definitions]
|
||||||
|
) {
|
||||||
|
|
||||||
|
def traverse (using lang: String)(f: (LangTag, Definitions) => Unit): Unit = {
|
||||||
|
langs.search(lang)
|
||||||
|
.getOrElse(langs.root)
|
||||||
|
.traverseTree(node =>
|
||||||
|
translations.get(node.langTag.lang)
|
||||||
|
.map(
|
||||||
|
f(node.langTag, _)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
def traverseWithKey (key: String)(using lang: String)(f: (LangTag, Option[String]) => Unit): Unit = {
|
||||||
|
this.traverse (using lang) { (langTag, definitions) =>
|
||||||
|
f(langTag, definitions.get(key))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def get (key: String)(using lang: String): Option[String] = {
|
||||||
|
boundary {
|
||||||
|
traverseWithKey(key) { (_, value) =>
|
||||||
|
if value.nonEmpty then
|
||||||
|
boundary.break(Some(value.get))
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def trans (key: String)(using lang: String): VarText =
|
||||||
|
VarText(get(key).getOrElse(s"#[$key@$lang]"))
|
||||||
|
|
||||||
|
def trans (key: String, args: Var*)(using lang: String): String =
|
||||||
|
trans(key).render(args*)
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user