From 9ddfa6539d3ae3d4ef50ffbcbee6e8e6055d00bb Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Sat, 20 Apr 2024 20:38:21 +0800 Subject: [PATCH] make http:sticker/id basic usable --- .../cono/morny/core/http/api/HttpStatus.scala | 30 ++++++++++++++++--- .../cono/morny/stickers_get/StickerType.scala | 24 +++++++++++++++ .../stickers_get/http/StickerService.scala | 13 +++++++- .../cono/morny/util/StringEnsure.scala | 8 +++++ 4 files changed, 70 insertions(+), 5 deletions(-) create mode 100644 src/main/scala/cc/sukazyo/cono/morny/stickers_get/StickerType.scala diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpStatus.scala b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpStatus.scala index 1fb9760..dfb21d8 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpStatus.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpStatus.scala @@ -2,23 +2,45 @@ 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.StringEnsure.firstLine 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` +import java.net.URLEncoder +import java.nio.charset.StandardCharsets + +object HttpStatus extends HttpStatus + 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 + e.getClass.getSimpleName.firstLine, + e.getMessage.firstLine, + URLEncoder.encode(e.toLogString, StandardCharsets.UTF_8) // todo: maybe extract this const ) + + /** Set HTTP Header that shows Morny's Internal Server Error related information. + * + * It will set the following headers, corresponding to this method's parameters, you + * can customize those values. + * - Morny-Internal-Error-Type + * - Morny-Internal-Error-Message + * - Morny-Internal-Error-Detail + * + * **Notice that ANY header value MUST NOT contains line breaking!** Or it will only + * returns an empty 500 response. + * + * @return the [[ResponseT]], with above headers added. + */ def setMornyInternalErrorHeader ( `Morny-Internal-Error-Type`: String, `Morny-Internal-Error-Message`: String, @@ -43,7 +65,7 @@ trait HttpStatus { .map(_.withContentType(`Content-Type`(MediaType.image.png))) /** 500 Internal Server Error */ - def MornyInternalServerError (): ResponseIO = + def MornyInternalServerError (): ResponseIO = InternalServerError(TelegramImages.IMG_500.get) .map(_.withContentType(`Content-Type`(MediaType.image.png))) diff --git a/src/main/scala/cc/sukazyo/cono/morny/stickers_get/StickerType.scala b/src/main/scala/cc/sukazyo/cono/morny/stickers_get/StickerType.scala new file mode 100644 index 0000000..5a3e702 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/stickers_get/StickerType.scala @@ -0,0 +1,24 @@ +package cc.sukazyo.cono.morny.stickers_get + +import java.nio.charset.StandardCharsets + +enum StickerType { + case WEBP + case WEBM + case PNG +} + +object StickerType { + + class UnknownStickerTypeException (message: String) + extends Exception (s"Unknown sticker type: $message") + +// @throws[UnknownStickerTypeException] + def check (stickerFile: Array[Byte]): StickerType = + val header = new String(stickerFile.take(50), StandardCharsets.UTF_8) + if header.contains("WEBP") then WEBP + else if header.contains("webm") then WEBM + else if header.contains("PNG") then PNG + else throw UnknownStickerTypeException("Unable to infer file type.") + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/stickers_get/http/StickerService.scala b/src/main/scala/cc/sukazyo/cono/morny/stickers_get/http/StickerService.scala index 5bf9989..3a737db 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/stickers_get/http/StickerService.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/stickers_get/http/StickerService.scala @@ -3,12 +3,15 @@ package cc.sukazyo.cono.morny.stickers_get.http import cats.effect.IO import cc.sukazyo.cono.morny.core.http.api.HttpService4Api import cc.sukazyo.cono.morny.core.MornyCoeur +import cc.sukazyo.cono.morny.stickers_get.StickerType +import cc.sukazyo.cono.morny.stickers_get.StickerType.UnknownStickerTypeException import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute import com.pengrad.telegrambot.request.GetFile import com.pengrad.telegrambot.TelegramBot -import org.http4s.HttpRoutes +import org.http4s.{HttpRoutes, MediaType} import org.http4s.dsl.io.* +import org.http4s.headers.`Content-Type` import java.io.IOException @@ -23,8 +26,16 @@ class StickerService (using coeur: MornyCoeur) extends HttpService4Api { try { given TelegramBot = coeur.account val file = response.file.getContent + val file_type: MediaType = StickerType.check(file) match + case StickerType.WEBP => MediaType.image.webp + case StickerType.WEBM => MediaType.video.webm + case StickerType.PNG => MediaType.image.png Ok(file) + .map(_.withContentType(`Content-Type`(file_type))) } catch { + case e: UnknownStickerTypeException => + MornyInternalServerError() + .map(_.setMornyInternalErrorHeader(e)) case e: IOException => MornyServiceUnavailable() .map(_.setMornyInternalErrorHeader(e)) diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala b/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala index c0121e3..5fb217c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala @@ -4,6 +4,14 @@ object StringEnsure { extension (str: String) { + /** Take the first line of this String. + * + * It will find the first `\n` (unicode 0x0a, aka. LF) or `\n` (unicode 0x0e, aka. CR) + * and take all the chars before it, and ignores all the contents after it. + */ + def firstLine : String = + str.takeWhile(c => (c != '\n') && (c != '\r')) + /** Ensure the string have a length not smaller that the given length. * * If the length of the string is smaller than the given length, then the string will be padded