From 8d04d6529c4fc94cde2451f63d565afd6f4bf58b Mon Sep 17 00:00:00 2001 From: Eyre_S Date: Thu, 15 Feb 2024 22:26:39 +0800 Subject: [PATCH] refactor bot execute method etc. - change Log.exceptionLog(throwable) to an extension method throwable.toLogString in UseThrowable - added SimpleCommandManager as the backend of both MornyCommandManager and UniMeowCommandManager - added Request().execute and Request().unsafeExecute() extensions. - change all the request execute using those extensions. - change some method with infix keyword. - change MornyTrusted methods using User/Chat object instead of a bare id. - update scala to 3.4.0-RC4 and fix infix warnings. --- build.sbt | 16 +++- project/MornyConfiguration.scala | 2 +- .../sukazyo/cono/morny/call_me/OnCallMe.scala | 36 +++++---- .../cono/morny/call_me/OnCallMsgSend.scala | 47 ++++++----- .../cc/sukazyo/cono/morny/core/Log.scala | 9 +-- .../sukazyo/cono/morny/core/MornyAbout.scala | 2 +- .../sukazyo/cono/morny/core/MornyCoeur.scala | 74 ++++++++--------- .../sukazyo/cono/morny/core/MornySystem.scala | 5 +- .../cono/morny/core/MornyTrusted.scala | 27 +++++-- .../sukazyo/cono/morny/core/ServerMain.scala | 30 +++---- .../core/bot/api/EventListenerManager.scala | 17 ++-- .../morny/core/bot/api/ITelegramQuery.scala | 5 +- .../core/bot/api/MornyCommandManager.scala | 28 +++---- .../core/bot/api/MornyQueryManager.scala | 9 ++- .../core/bot/api/SimpleCommandManager.scala | 21 +++++ .../core/bot/command/DirectMsgClear.scala | 32 ++++---- .../morny/core/bot/command/MornyHellos.scala | 10 ++- .../core/bot/command/MornyInfoOnStart.scala | 9 ++- .../core/bot/command/MornyInformation.scala | 36 +++++---- .../core/bot/command/MornyManagers.scala | 30 ++++--- .../core/bot/event/MornyOnInlineQuery.scala | 17 ++-- .../bot/event/MornyOnTelegramCommand.scala | 14 ++-- .../MornyOnUpdateTimestampOffsetLock.scala | 2 +- .../morny/core/http/api/HttpService4Api.scala | 4 +- .../http/api/MornyHttpServerContext.scala | 2 + .../internal/MornyHttpServerContextImpl.scala | 12 ++- .../logging/MornyFormatterConsole.scala | 3 - .../internal/logging/MornyLoggerBase.scala | 2 +- .../cono/morny/data/MornyInformation.scala | 2 +- .../cono/morny/data/TelegramImages.scala | 2 +- .../cono/morny/encrypt_tool/Encryptor.scala | 50 +++++++----- .../sukazyo/cono/morny/ip186/BotCommand.scala | 16 ++-- .../cono/morny/ip186/IP186QueryHandler.scala | 2 +- .../medication_timer/MedicationTimer.scala | 22 ++--- .../ModuleMedicationTimer.scala | 6 +- .../cono/morny/morny_misc/MornyJrrp.scala | 2 +- .../cono/morny/morny_misc/MornyOldJrrp.scala | 7 +- .../cono/morny/morny_misc/Testing.scala | 7 +- .../cono/morny/nbnhhsh/CommandNbnhhsh.scala | 32 ++++---- .../cono/morny/nbnhhsh/NbnhhshQuery.scala | 2 +- .../OnQuestionMarkReply.scala | 11 ++- .../randomize_somthing/OnUserRandom.scala | 19 +++-- .../sukazyo/cono/morny/reporter/Module.scala | 8 +- .../cono/morny/reporter/MornyReport.scala | 29 +++---- .../slash_action/OnUserSlashAction.scala | 16 ++-- .../social_share/ModuleSocialShare.scala | 1 - .../social_share/api/SocialContent.scala | 22 ++--- .../social_share/command/GetSocial.scala | 7 +- .../social_share/event/OnGetSocial.scala | 25 +++--- .../external/bilibili/BiliTool.scala | 4 +- .../external/bilibili/BilibiliForms.scala | 2 +- .../social_share/external/weibo/MApi.scala | 4 +- .../query/ShareToolBilibili.scala | 7 +- .../query/ShareToolSocialContent.scala | 6 +- .../social_share/query/ShareToolTwitter.scala | 3 +- .../stickers_get/http/StickerService.scala | 8 +- .../cono/morny/tele_utils/InlineRawText.scala | 2 +- .../event_hack/CommandEventHack.scala | 12 ++- .../tele_utils/event_hack/EventHacker.scala | 13 +-- .../tele_utils/user_info/CommandGetUser.scala | 25 +++--- .../user_info/InlineMyInformation.scala | 4 +- .../uni_meow/BotEventUniMeowTrigger.scala | 10 +-- .../uni_meow/UniMeowCommandManager.scala | 14 +--- .../cc/sukazyo/cono/morny/uni_meow/创.scala | 7 +- .../cc/sukazyo/cono/morny/uni_meow/喵呜.scala | 10 ++- .../sukazyo/cono/morny/uni_meow/私わね.scala | 7 +- .../cono/morny/util/CommonEncrypt.scala | 18 ++--- .../cono/morny/util/EpochDateTime.scala | 23 +++++- .../sukazyo/cono/morny/util/FileUtils.scala | 2 +- .../cono/morny/util/GivenContext.scala | 28 +++---- .../cono/morny/util/StringEnsure.scala | 57 ++++++++++++- .../sukazyo/cono/morny/util/SttpPublic.scala | 19 ++++- .../cc/sukazyo/cono/morny/util/UseMath.scala | 4 +- .../sukazyo/cono/morny/util/UseRandom.scala | 4 +- .../sukazyo/cono/morny/util/UseStacks.scala | 3 +- .../cono/morny/util/UseThrowable.scala | 16 ++++ .../cono/morny/util/schedule/Scheduler.scala | 12 +-- .../cono/morny/util/tgapi/InputCommand.scala | 2 +- .../morny/util/tgapi/TelegramExtensions.scala | 81 +++++++++++++++++-- .../formatting/TelegramParseEscape.scala | 6 +- .../cono/morny/util/time/WatchDog.scala | 4 +- .../cc/sukazyo/cono/morny/MornyCLI.scala | 4 +- .../test/daemon/MedicationTimerTest.scala | 2 +- .../morny/test/utils/EpochDateTimeTest.scala | 4 +- .../test/utils/schedule/TaskBasicTest.scala | 2 +- 85 files changed, 764 insertions(+), 454 deletions(-) create mode 100644 src/main/scala/cc/sukazyo/cono/morny/core/bot/api/SimpleCommandManager.scala create mode 100644 src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala diff --git a/build.sbt b/build.sbt index 7e298be..87ec014 100644 --- a/build.sbt +++ b/build.sbt @@ -3,7 +3,7 @@ aether.AetherKeys.aetherOldVersionMethod := true ThisBuild / organization := "cc.sukazyo" ThisBuild / organizationName := "A.C. Sukazyo Eyre" -ThisBuild / scalaVersion := "3.3.1" +ThisBuild / scalaVersion := "3.4.0-RC4" resolvers ++= Seq( "-ws-releases" at "https://mvn.sukazyo.cc/releases" @@ -56,6 +56,20 @@ lazy val root = (project in file(".")) "-target", "17" ), autoAPIMappings := true, + apiMappings ++= { + def mappingsFor(organization: String, names: List[String], location: String, revision: String => String = identity): Seq[(File, URL)] = + for { + entry: Attributed[File] <- (Compile / fullClasspath).value + module: ModuleID <- entry.get(moduleID.key) + if module.organization == organization + if names.exists(module.name.startsWith) + } yield entry.data -> url(location.format(revision(module.revision))) + val mappings: Seq[(File, URL)] = Seq( + mappingsFor("org.scala-lang", List("scala-library"), "https://scala-lang.org/api/%s/"), + mappingsFor("com.github.pengrad", "java-telegram-bot-api"::Nil, "https://jitpack.io/com/github/pengrad/java-telegram-bot-api/6.3.0/javadoc/"), + ).flatten + mappings.toMap + }, assemblyMergeStrategy := { case module if module endsWith "module-info.class" => MergeStrategy.concat diff --git a/project/MornyConfiguration.scala b/project/MornyConfiguration.scala index e24db4e..8b35ed8 100644 --- a/project/MornyConfiguration.scala +++ b/project/MornyConfiguration.scala @@ -8,7 +8,7 @@ object MornyConfiguration { val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono" val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s" - val VERSION = "2.0.0-alpha13" + val VERSION = "2.0.0-alpha14" val VERSION_DELTA: Option[String] = None val CODENAME = "guanggu" diff --git a/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMe.scala b/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMe.scala index 0e934b7..7783e63 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMe.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMe.scala @@ -4,14 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{Chat, Message, User} import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker} - -import scala.language.postfixOps +import com.pengrad.telegrambot.TelegramBot class OnCallMe (using coeur: MornyCoeur) extends EventListener { + private given TelegramBot = coeur.account private val me = coeur.config.trustedMaster @@ -30,65 +30,70 @@ class OnCallMe (using coeur: MornyCoeur) extends EventListener { requestItem(update.message.from, "Hana Paresu") case "dinner" | "lunch" | "breakfast" | "meal" | "eating" | "安妮今天吃什么" => requestLastDinner(update.message) - case cc if cc startsWith "cc::" => + case cc if cc `startsWith` "cc::" => requestCustom(update.message) case _ => return; if success then - coeur.account exec SendSticker( + SendSticker( update.message.chat.id, TelegramStickers ID_SENT ).replyToMessageId(update.message.messageId) + .unsafeExecute else - coeur.account exec SendSticker( + SendSticker( update.message.chat.id, TelegramStickers ID_501 ).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Boolean = - coeur.account exec SendMessage( + SendMessage( me, s"""request $itemHTML |from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}""" .stripMargin ).parseMode(ParseMode HTML) + .unsafeExecute true private def requestLastDinner (req: Message): Boolean = { if coeur.config.dinnerChatId == -1 then return false var isAllowed = false var lastDinnerData: Message|Null = null - if (coeur.trusted isTrusted_dinnerReader req.from.id) { + if (coeur.trusted isTrust4dinner req.from) { // todo: have issues // i dont want to test it anymore... it might be deprecated soon - lastDinnerData = (coeur.account exec GetChat(coeur.config.dinnerChatId)).chat.pinnedMessage - val sendResp = coeur.account exec ForwardMessage( + lastDinnerData = GetChat(coeur.config.dinnerChatId).unsafeExecute.chat.pinnedMessage + val sendResp = ForwardMessage( req.from.id, lastDinnerData.forwardFromChat.id, lastDinnerData.forwardFromMessageId - ) + ).unsafeExecute import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration} import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - def lastDinner_dateMillis: EpochMillis = EpochMillis fromEpochSeconds lastDinnerData.forwardDate - coeur.account exec SendMessage( + def lastDinner_dateMillis: EpochMillis = EpochMillis fromSeconds lastDinnerData.forwardDate + SendMessage( req.from.id, "on %s [UTC+8]\n- %s before".formatted( h(formatDate(lastDinner_dateMillis, 8)), h(formatDuration(System.currentTimeMillis - lastDinner_dateMillis)) ) ).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId) + .unsafeExecute isAllowed = true } else { - coeur.account exec SendSticker( + SendSticker( req.from.id, TelegramStickers ID_403 ).replyToMessageId(req.messageId) + .unsafeExecute } import Math.abs requestItem( @@ -100,7 +105,8 @@ class OnCallMe (using coeur: MornyCoeur) extends EventListener { private def requestCustom (message: Message): Boolean = requestItem(message.from, "[???]") - coeur.account exec ForwardMessage(me, message.chat.id, message.messageId) + ForwardMessage(me, message.chat.id, message.messageId) + .unsafeExecute true } diff --git a/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMsgSend.scala b/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMsgSend.scala index 53c7bd2..ef968e4 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMsgSend.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/call_me/OnCallMsgSend.scala @@ -3,16 +3,18 @@ package cc.sukazyo.cono.morny.call_me import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.data.TelegramStickers -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.* import com.pengrad.telegrambot.model.{Chat, Message, MessageEntity} import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{GetChat, SendMessage, SendSticker} +import com.pengrad.telegrambot.TelegramBot import scala.collection.mutable.ArrayBuffer import scala.language.postfixOps import scala.util.matching.Regex class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { + private given TelegramBot = coeur.account private val REGEX_MSG_SENDREQ_DATA_HEAD: Regex = "^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$"r @@ -25,12 +27,12 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { def toSendMessage (target_override: Long|Null = null): SendMessage = val useTarget = if target_override == null then targetId else target_override val sendMessage = SendMessage(useTarget, message) - if entities ne null then sendMessage.entities(entities:_*) + if entities ne null then sendMessage.entities(entities*) if parseMode ne null then sendMessage.parseMode(parseMode) sendMessage } private object MessageToSend: - def from (raw: Message): MessageToSend = { + infix def from (raw: Message): MessageToSend = { raw.text match case REGEX_MSG_SENDREQ_DATA_HEAD(_target, _parseMode, _body) => val target = _target toLong @@ -57,15 +59,15 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { val message = update.message - if message.chat.`type` != Chat.Type.Private then return; - if message.text eq null then return; - if !(message.text startsWith "*msg") then return; + if message.chat.`type` != Chat.Type.Private then return + if message.text eq null then return + if !(message.text `startsWith` "*msg") then return - if (!(coeur.trusted isTrusted message.from.id)) - coeur.account exec SendSticker( + if (!(coeur.trusted isTrust message.from)) + SendSticker( message.chat.id, TelegramStickers ID_403 - ).replyToMessageId(message.messageId) + ).replyToMessageId(message.messageId).unsafeExecute event.setEventOk return; @@ -74,21 +76,23 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { if (message.replyToMessage eq null) { answer404; return } val messageToSend = MessageToSend from message.replyToMessage if ((messageToSend eq null) || (messageToSend.message eq null)) { answer404; return } - val sendResponse = coeur.account execute messageToSend.toSendMessage() + val sendResponse = messageToSend.toSendMessage().execute if (sendResponse isOk) { - coeur.account exec SendSticker( + SendSticker( update.message.chat.id, TelegramStickers ID_SENT ).replyToMessageId(update.message.messageId) + .unsafeExecute } else { - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, // language=html s"""${sendResponse.errorCode} FAILED |${sendResponse.description}""" .stripMargin ).replyToMessageId(update.message.messageId).parseMode(ParseMode HTML) + .unsafeExecute } event.setEventOk @@ -101,14 +105,14 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { if (message.text == "*msg") if message.replyToMessage eq null then { answer404; return } else message.replyToMessage - else if (message.text startsWith "*msg") + else if (message.text `startsWith` "*msg") message else { answer404; return } val _toSend = MessageToSend from raw if _toSend eq null then { answer404; return } else _toSend - val targetChatResponse = coeur.account execute GetChat(messageToSend.targetId) + val targetChatResponse = GetChat(messageToSend.targetId).execute if (targetChatResponse isOk) { def getChatDescriptionHTML (chat: Chat): String = import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* @@ -117,41 +121,46 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { s"""${h(chat.id toString)}@${h(chat.`type`.name)}${if (chat.`type` != Chat.Type.Private) ":::" else ""} |${chat.typeTag} ${h(chat.safe_name)} ${chat.safe_linkHTML}""" .stripMargin - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, getChatDescriptionHTML(targetChatResponse.chat) ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) + .unsafeExecute } else { - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, // language=html s"""${targetChatResponse.errorCode} FAILED |${targetChatResponse.description}""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) + .unsafeExecute } if messageToSend.message eq null then { answer404; return } - val testSendResponse = coeur.account execute + val testSendResponse = messageToSend.toSendMessage(update.message.chat.id).replyToMessageId(update.message.messageId) + .execute if (!(testSendResponse isOk)) - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, // language=html s"""${testSendResponse.errorCode} FAILED |${testSendResponse.description}""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } private def answer404 (using event: EventEnv): Unit = - coeur.account exec SendSticker( + SendSticker( event.update.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.update.message.messageId) + .unsafeExecute event.setEventOk } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/Log.scala b/src/main/scala/cc/sukazyo/cono/morny/core/Log.scala index 536dc85..134cd1d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/Log.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/Log.scala @@ -4,8 +4,6 @@ import cc.sukazyo.cono.morny.core.internal.logging.{MornyFormatterConsole, Morny import cc.sukazyo.messiva.appender.ConsoleAppender import cc.sukazyo.messiva.log.LogLevels -import java.io.{PrintWriter, StringWriter} - object Log { val logger: MornyLoggerBase = MornyLoggerBase( @@ -13,7 +11,7 @@ object Log { MornyFormatterConsole() ) ) - logger minLevel LogLevels.INFO + logger.minLevel(LogLevels.INFO) def debug: Boolean = logger.levelSetting.minLevel.level <= LogLevels.DEBUG.level @@ -21,9 +19,4 @@ object Log { if is then logger.minLevel(LogLevels.ALL) else logger.minLevel(LogLevels.INFO) - def exceptionLog (e: Throwable): String = - val stackTrace = StringWriter() - e printStackTrace PrintWriter(stackTrace) - stackTrace toString - } 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 36dbed0..22002d0 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.pack `getResource` "texts/server-hello.txt" readAsString } 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 8a75a02..f58c148 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala @@ -1,6 +1,6 @@ package cc.sukazyo.cono.morny.core -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur.* import cc.sukazyo.cono.morny.core.bot.api.{EventListenerManager, MornyCommandManager, MornyQueryManager} import cc.sukazyo.cono.morny.core.bot.event.{MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock} @@ -11,6 +11,7 @@ import cc.sukazyo.cono.morny.util.schedule.Scheduler import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis import cc.sukazyo.cono.morny.util.time.WatchDog import cc.sukazyo.cono.morny.util.GivenContext +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.request.GetMe @@ -115,7 +116,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes val externalContext: GivenContext = GivenContext() import cc.sukazyo.cono.morny.util.dataview.Table.format as fmtTable - logger info + logger `info` s"""The following Modules have been added to current Morny: |${fmtTable( ("Module ID" :: "Module Name" :: "Module Version" :: Nil)::Nil ::: @@ -125,19 +126,19 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes ///>>> BLOCK START instance configure & startup stage 1 - logger info "Coeur starting..." + logger `info` "Coeur starting..." private var initializeContext = GivenContext() import cc.sukazyo.cono.morny.util.StringEnsure.deSensitive - logger info s"args key:\n ${config.telegramBotKey deSensitive 4}" + logger `info` s"args key:\n ${config.telegramBotKey.deSensitive(4)}" if config.telegramBotUsername ne null then - logger info s"login as:\n ${config.telegramBotUsername}" + logger `info` s"login as:\n ${config.telegramBotUsername}" private val __loginResult: LoginResult = login() match case some: Some[LoginResult] => some.get case None => - logger error "Login to bot failed." - System exit -1 + logger `error` "Login to bot failed." + System `exit` -1 throw RuntimeException() initializeContext << __loginResult @@ -221,7 +222,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes val watchDog: WatchDog = WatchDog("watch-dog", 1000, 1500, { (consumed, _) => import cc.sukazyo.cono.morny.util.CommonFormat.formatDuration as f - logger warn + logger `warn` s"""Can't keep up! is the server overloaded or host machine fall asleep? | current tick takes ${f(consumed)} to complete.""".stripMargin tasks.notifyIt() @@ -238,9 +239,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes ///>>> BLOCK START instance configure & startup stage 2 - logger info "done initialize." + logger `info` "done initialize." if testRun then - logger info "done test run, exiting." + logger `info` "done test run, exiting." this.exit(0, TestRun) // Coeur Starting Pre @@ -250,10 +251,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes modules.foreach(it => it.onStarting(OnStartingContext( initializeContext))) - logger info "start http server" + logger `info` "start http server" val http: HttpServer = _httpServerContext.start _httpServerContext = null - logger info "start telegram event listening" + logger `info` "start telegram event listening" import com.pengrad.telegrambot.TelegramException account.setUpdatesListener(eventManager, (e: TelegramException) => { @@ -267,10 +268,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes if (e.response != null) { import com.google.gson.GsonBuilder - logger error + logger `error` s"""Failed get updates: ${e.getMessage} | server responses: - |${GsonBuilder().setPrettyPrinting().create.toJson(e.response) indent 4} + |${GsonBuilder().setPrettyPrinting().create.toJson(e.response).indent(4)} |""".stripMargin externalContext.consume[MornyReport](_.exception(e, "Failed get updates.")) } @@ -291,11 +292,11 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes current = current.getCause log += s" caused by: ${current.getClass.getSimpleName}: ${current.getMessage}" } - logger error Message(log mkString "\n") + logger `error` Message(log mkString "\n") case e_other => - logger error + logger `error` s"""Failed get updates: - |${exceptionLog(e_other) indent 3}""".stripMargin + |${e_other.toLogString `indent` 3}""".stripMargin externalContext.consume[MornyReport](_.exception(e_other, "Failed get updates.")) } @@ -306,17 +307,17 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes initializeContext))) if config.commandLoginRefresh then - logger info "resetting telegram command list" + logger `info` "resetting telegram command list" commands.automaticTGListUpdate() initializeContext = null - logger info "Coeur start complete." + logger `info` "Coeur start complete." ///<<< BLOCK END instance configure & startup stage 2 def saveDataAll(): Unit = { modules.foreach(it => it.onRoutineSavingData) - logger notice "done all save action." + logger `notice` "done all save action." } private def exitCleanup (): Unit = { @@ -325,9 +326,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes modules.foreach(it => it.onExiting) account.removeGetUpdatesListener() - logger info "stopped bot update listener" + logger `info` "stopped bot update listener" tasks.waitForStop() - logger info s"morny tasks stopped: remains ${tasks.amount} tasks not be executed" + logger `info` s"morny tasks stopped: remains ${tasks.amount} tasks not be executed" // Morny Exiting Post if config.commandLogoutClear then @@ -335,10 +336,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes modules.foreach(it => it.onExitingPost) account.shutdown() - logger info "stopped bot account" + logger `info` "stopped bot account" // Morny Exited modules.foreach(it => it.onExited) - logger info "done exit cleanup\nMorny will EXIT now" + logger `info` "done exit cleanup\nMorny will EXIT now" } @@ -348,7 +349,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes def exit (status: Int, reason: AnyRef): Unit = whileExit_reason = Some(reason) - System exit status + System `exit` status private case class LoginResult(account: TelegramBot, username: String, userid: Long) @@ -358,15 +359,15 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes var api_bot = config.telegramBotApiServer var api_file = config.telegramBotApiServer4File if (api_bot ne null) - if api_bot endsWith "/" then api_bot = api_bot dropRight 1 - if !(api_bot endsWith "/bot") then api_bot += "/bot" + if api_bot `endsWith` "/" then api_bot = api_bot dropRight 1 + if !(api_bot `endsWith` "/bot") then api_bot += "/bot" builder.apiUrl(api_bot) if (api_file ne null) - if api_file endsWith "/file/" then api_file = api_file dropRight 1 - if !(api_file endsWith "/file/bot") then api_file += "/file/bot" + if api_file `endsWith` "/file/" then api_file = api_file dropRight 1 + if !(api_file `endsWith` "/file/bot") then api_file += "/file/bot" builder.apiUrl(api_bot) if ((api_bot ne null) || (api_file ne null)) - logger info + logger `info` s"""Telegram bot api set to: |- bot: $api_bot |- file: $api_file""" @@ -374,21 +375,22 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes val account = builder build - logger info "Trying to login..." + logger `info` "Trying to login..." boundary[Option[LoginResult]] { for (i <- 0 to 3) { - if i > 0 then logger info "retrying..." + if i > 0 then logger `info` "retrying..." try { - val remote = (account execute GetMe()).user + import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute + val remote = GetMe().execute(using account).user if ((config.telegramBotUsername ne null) && config.telegramBotUsername != remote.username) throw RuntimeException(s"Required the bot @${config.telegramBotUsername} but @${remote.username} logged in") - logger info s"Succeed logged in to @${remote.username}" + logger `info` s"Succeed logged in to @${remote.username}" break(Some(LoginResult(account, remote.username, remote.id))) } catch case r: boundary.Break[Option[LoginResult]] => throw r case e => - logger error - s"""${exceptionLog(e)} + logger `error` + s"""${e.toLogString} |login failed""" .stripMargin } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornySystem.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornySystem.scala index 11d0012..fe0c1ce 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornySystem.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornySystem.scala @@ -1,10 +1,11 @@ package cc.sukazyo.cono.morny.core -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.internal.BuildConfigField import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis import cc.sukazyo.cono.morny.util.FileUtils import cc.sukazyo.cono.morny.BuildConfig +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import java.io.IOException import java.net.URISyntaxException @@ -38,7 +39,7 @@ object MornySystem { case _: (IOException|URISyntaxException) => "" case n: NoSuchAlgorithmException => - logger error exceptionLog(n) + logger `error` n.toLogString // MornyReport.exception(n, "") // todo: will not implemented "" } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyTrusted.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyTrusted.scala index 60929a3..f9c4262 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyTrusted.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyTrusted.scala @@ -5,20 +5,33 @@ import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.{LimboChat, LimboUser import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Chat.* import com.pengrad.telegrambot.model.ChatMember.Status import com.pengrad.telegrambot.TelegramBot +import com.pengrad.telegrambot.model.User class MornyTrusted (using coeur: MornyCoeur)(using config: MornyConfig) { if config.trustedMaster == -1 then - logger warn "You have not set your Morny's master.\n it may have some issues on controlling your bot." + logger `warn` "You have not set your Morny's master.\n it may have some issues on controlling your bot." - def isTrusted (userId: Long): Boolean = + /** If the user can be trusted. + * @since 2.0.0 + * @param user The user that will be check if it is in this trust list. Only user's id will be used, + * so if you don't have a user instance, you can safely use [[LimboUser]]. + * @return `true` if this user can be trusted, `false` otherwise. + */ + infix def isTrust (user: User): Boolean = given TelegramBot = coeur.account - if userId == config.trustedMaster then true + if user.id == config.trustedMaster then true else if config.trustedChat == -1 then false - else LimboChat(config.trustedChat) memberHasPermission(LimboUser(userId), Status.administrator) + else LimboChat(config.trustedChat).memberHasPermission(user, Status.administrator) - def isTrusted_dinnerReader (userId: Long): Boolean = - if userId == config.trustedMaster then true - else config.dinnerTrustedReaders contains userId + /** If this user can be trusted to read the dinner messages. + * @since 2.0.0 + * @param user The user that will be check if it is in this trust list. Only user's id will be used, + * so if you don't have a user instance, you can safely use [[LimboUser]]. + * @return `true` if this user can read dinners, `false` otherwise. + */ + infix def isTrust4dinner (user: User): Boolean = + if user.id == config.trustedMaster then true + else config.dinnerTrustedReaders `contains` user.id } 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 ce238b7..3a4147e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/ServerMain.scala @@ -11,7 +11,7 @@ import scala.collection.mutable.ArrayBuffer object ServerMain { val tz: TimeZone = TimeZone getDefault - val tz_offset: ZoneOffset = ZoneOffset ofTotalSeconds(tz.getRawOffset / 1000) + val tz_offset: ZoneOffset = ZoneOffset `ofTotalSeconds` (tz.getRawOffset / 1000) private val THREAD_MORNY_INIT: String = "morny-init" @@ -54,7 +54,7 @@ object ServerMain { case "--report-to" => i += 1; config.reportToChat = args(i) toLong case "--report-zone" => i += 1; config.reportZone = TimeZone.getTimeZone(args(i)) - case "--trusted-reader-dinner" | "-trsd" => i += 1; config.dinnerTrustedReaders add(args(i) toLong) + case "--trusted-reader-dinner" | "-trsd" => i += 1; config.dinnerTrustedReaders `add` (args(i) toLong) case "--dinner-chat" | "-chd" => i += 1; config.dinnerChatId = args(i) toLong case "--http-listen-port" | "-hp" => @@ -67,8 +67,8 @@ object ServerMain { config.medicationTimerUseTimezone = ZoneOffset.ofHours(args(i) toInt) case "--medication-notify-times" | "-medt" => i += 1 - for (u <- args(i) split ",") { - config.medicationNotifyAt add(u toInt) + for (u <- args(i) `split` ",") { + config.medicationNotifyAt `add` (u toInt) } case "--auto-cmd-list" | "-ca" => config.commandLoginRefresh = true @@ -87,8 +87,8 @@ object ServerMain { var propToken: String = null var propTokenKey: String = null for (iKey <- MornyConfig.PROP_TOKEN_KEY) { - if ((System getenv iKey) != null) { - propToken = System getenv iKey + if ((System `getenv` iKey) != null) { + propToken = System `getenv` iKey propTokenKey = iKey } } @@ -98,27 +98,27 @@ object ServerMain { /// process startup params - like startup mode /// - if (showHello) logger info MornyAbout.MORNY_PREVIEW_IMAGE_ASCII + if (showHello) logger `info` MornyAbout.MORNY_PREVIEW_IMAGE_ASCII if (mode_echoHello) return - if (unknownArgs.nonEmpty) logger warn + if (unknownArgs.nonEmpty) logger `warn` s"""Can't understand arg to some meaning | ${unknownArgs mkString "\n "}""" .stripMargin - if (deprecatedArgs.nonEmpty) logger warn + if (deprecatedArgs.nonEmpty) logger `warn` s"""Those arguments have been deprecated: | ${deprecatedArgs map((d, n) => s"$d : use $n instead") mkString "\n "} |""".stripMargin if (Log debug) - logger warn + logger `warn` """Debug log output enabled. | It may lower your performance, make sure that you are not in production environment.""" .stripMargin if (mode_echoVersion) { - logger info + logger `info` s"""Morny Cono Version |- version : | Morny ${MornySystem.CODENAME toUpperCase} @@ -148,14 +148,14 @@ object ServerMain { } - logger info + logger `info` s"""ServerMain.java Loaded >>> |- version ${MornySystem.VERSION_FULL} |- Morny ${MornySystem.CODENAME toUpperCase} |- <${MornySystem.getJarMD5}> [${MornySystem.CODE_TIMESTAMP}]""".stripMargin // due to [[MornyFormatterConsole]] will use a localized time, it will output to the log - logger info s"logging time will use time-zone ${tz.getID} ($tz_offset)" + logger `info` s"logging time will use time-zone ${tz.getID} ($tz_offset)" /// /// Check Coeur arguments @@ -164,10 +164,10 @@ object ServerMain { if (propToken != null) { config.telegramBotKey = propToken - logger info s"Parameter set by EnvVar $$$propTokenKey" + logger `info` s"Parameter set by EnvVar $$$propTokenKey" } - Thread.currentThread setName THREAD_MORNY_INIT + Thread.currentThread `setName` THREAD_MORNY_INIT try MornyCoeur( diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala index 6909829..570bc2c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/EventListenerManager.scala @@ -1,15 +1,15 @@ package cc.sukazyo.cono.morny.core.bot.api import cc.sukazyo.cono.morny.core.{Log, MornyCoeur} -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import com.google.gson.GsonBuilder import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.UpdatesListener import scala.collection.mutable -import scala.language.postfixOps /** Contains a [[scala.collection.mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]]. * @@ -21,13 +21,16 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener { private val listeners = mutable.Queue.empty[EventListener] + infix def register (listener: EventListener): Unit = + this.listeners += listener + def register (listeners: EventListener*): Unit = this.listeners ++= listeners private class EventRunner (using update: Update) extends Thread { - this setName s"upd-${update.updateId()}-nn" + this `setName` s"upd-${update.updateId()}-nn" private def updateThreadName (t: String): Unit = - this setName s"upd-${update.updateId()}-$t" + this `setName` s"upd-${update.updateId()}-$t" override def run (): Unit = { @@ -80,15 +83,15 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener { } catch case e => { val errorMessage = StringBuilder() errorMessage ++= "Event throws unexpected exception:\n" - errorMessage ++= (exceptionLog(e) indent 4) + errorMessage ++= (e.toLogString `indent` 4) e match case actionFailed: EventRuntimeException.ActionFailed => errorMessage ++= "\ntg-api action: response track: " errorMessage ++= (GsonBuilder().setPrettyPrinting().create().toJson( actionFailed.response - ) indent 4) ++= "\n" + ) `indent` 4) ++= "\n" case _ => - logger error errorMessage.toString + logger `error` errorMessage.toString coeur.externalContext.consume[MornyReport](_.exception(e, "on event running")) } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/ITelegramQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/ITelegramQuery.scala index 52e3036..51a5ee9 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/ITelegramQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/ITelegramQuery.scala @@ -1,12 +1,9 @@ package cc.sukazyo.cono.morny.core.bot.api -import cc.sukazyo.cono.morny.core.MornyCoeur import com.pengrad.telegrambot.model.Update -import javax.annotation.Nullable - trait ITelegramQuery { - def query (event: Update): List[InlineQueryUnit[_]] | Null + def query (event: Update): List[InlineQueryUnit[?]] | Null } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyCommandManager.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyCommandManager.scala index 1cee14d..783c784 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyCommandManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyCommandManager.scala @@ -2,13 +2,12 @@ package cc.sukazyo.cono.morny.core.bot.api import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur -import cc.sukazyo.cono.morny.core.bot.api.{ISimpleCommand, ITelegramCommand} -import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{BotCommand, DeleteMyCommands, Update} import com.pengrad.telegrambot.request.{SendSticker, SetMyCommands} +import com.pengrad.telegrambot.TelegramBot import scala.collection.mutable import scala.collection.mutable.ArrayBuffer @@ -17,14 +16,8 @@ import scala.language.postfixOps object MornyCommandManager: type CommandMap = mutable.SeqMap[String, ISimpleCommand] -class MornyCommandManager (using coeur: MornyCoeur) { - - private val commands: CommandMap = mutable.SeqMap.empty - def register [T <: ISimpleCommand] (commands: T*): Unit = - for (i <- commands) - this.commands += (i.name -> i) - for (alias <- i.aliases) - this.commands += (alias.name -> i) +class MornyCommandManager (using coeur: MornyCoeur) extends SimpleCommandManager { + private given TelegramBot = coeur.account def execute (using command: InputCommand, event: Update): Boolean = { if (commands contains command.command) @@ -36,25 +29,28 @@ class MornyCommandManager (using coeur: MornyCoeur) { private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = { if command.target eq null then false else - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) + .unsafeExecute true } def automaticTGListUpdate (): Unit = { val listing = commands_toTelegramList automaticTGListRemove() - coeur.account exec SetMyCommands(listing:_*) - logger notice + SetMyCommands(listing*) + .unsafeExecute + logger `notice` s"""automatic updated telegram command list : |${commandsTelegramList_toString(listing)}""".stripMargin } def automaticTGListRemove (): Unit = { - coeur.account exec DeleteMyCommands() - logger notice "cleaned up command list" + DeleteMyCommands() + .unsafeExecute + logger `notice` "cleaned up command list" } private def commandsTelegramList_toString (list: Array[BotCommand]): String = diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyQueryManager.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyQueryManager.scala index 11da0f7..7381c54 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyQueryManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/MornyQueryManager.scala @@ -10,13 +10,16 @@ class MornyQueryManager (using MornyCoeur) { private val queries = mutable.Queue.empty[ITelegramQuery] + infix def register (query: ITelegramQuery): Unit = + this.queries += query + def register (queries: ITelegramQuery*): Unit = this.queries ++= queries - def query (event: Update): List[InlineQueryUnit[_]] = { - val results = ListBuffer[InlineQueryUnit[_]]() + def query (event: Update): List[InlineQueryUnit[?]] = { + val results = ListBuffer[InlineQueryUnit[?]]() for (instance <- queries) { - val r = instance query event + val r = instance `query` event if (r != null) results ++= r } results.result() diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/SimpleCommandManager.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/SimpleCommandManager.scala new file mode 100644 index 0000000..31d4732 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/api/SimpleCommandManager.scala @@ -0,0 +1,21 @@ +package cc.sukazyo.cono.morny.core.bot.api + +import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap + +import scala.collection.mutable + +trait SimpleCommandManager { + + protected val commands: CommandMap = mutable.SeqMap.empty + + protected def doRegister [T <: ISimpleCommand](it: T): Unit = + this.commands += (it.name -> it) + for (alias <- it.aliases) + this.commands += (alias.name -> it) + + infix def register [T <: ISimpleCommand] (command: T): Unit = + doRegister(command) + def register [T <: ISimpleCommand] (commands: T*): Unit = + for (command <- commands) doRegister(command) + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/DirectMsgClear.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/DirectMsgClear.scala index c740062..32e65b1 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/DirectMsgClear.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/DirectMsgClear.scala @@ -5,28 +5,30 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{Chat, Update} import com.pengrad.telegrambot.request.{DeleteMessage, GetChatMember, SendSticker} +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand { + private given TelegramBot = coeur.account override val name: String = "r" override val aliases: List[ICommandAlias] = Nil override def execute (using command: InputCommand, event: Update): Unit = { - logger debug "executing command /r" - if (event.message.replyToMessage == null) return; - logger trace "message is a reply" - if (event.message.replyToMessage.from.id != coeur.userid) return; - logger trace "message replied is from me" - if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return; - logger trace "message is not outdated(48 hrs ago)" + logger `debug` "executing command /r" + if (event.message.replyToMessage == null) return + logger `trace` "message is a reply" + if (event.message.replyToMessage.from.id != coeur.userid) return + logger `trace` "message replied is from me" + if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return + logger `trace` "message is not outdated(48 hrs ago)" - val isTrusted = coeur.trusted isTrusted event.message.from.id + val isTrusted = coeur.trusted isTrust event.message.from // todo: // it does not work. due to the Telegram Bot API doesn't provide // nested replyToMessage, so currently the trusted check by @@ -38,22 +40,22 @@ class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand { if (isTrusted || _isReplyTrusted) { - coeur.account exec DeleteMessage( + DeleteMessage( event.message.chat.id, event.message.replyToMessage.messageId - ) + ).unsafeExecute def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private def _isPermission: Boolean = - (coeur.account exec GetChatMember(event.message.chat.id, event.message.from.id)) + GetChatMember(event.message.chat.id, event.message.from.id).unsafeExecute .chatMember.canDeleteMessages if (_isPrivate || _isPermission) { - coeur.account exec DeleteMessage(event.message.chat.id, event.message.messageId) + DeleteMessage(event.message.chat.id, event.message.messageId).unsafeExecute } - } else coeur.account exec SendSticker( + } else SendSticker( event.message.chat.id, TelegramStickers ID_403 - ).replyToMessageId(event.message.messageId) + ).replyToMessageId(event.message.messageId).unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyHellos.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyHellos.scala index b6ed789..e71236e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyHellos.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyHellos.scala @@ -5,13 +5,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.core.bot.api.ICommandAlias.ListedAlias import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendSticker +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class MornyHellos (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account object On extends ITelegramCommand { @@ -21,10 +23,11 @@ class MornyHellos (using coeur: MornyCoeur) { override val description: String = "检查是否在线" override def execute (using command: InputCommand, event: Update): Unit = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_ONLINE_STATUS_RETURN ).replyToMessageId(event.message.messageId) + .unsafeExecute } @@ -36,10 +39,11 @@ class MornyHellos (using coeur: MornyCoeur) { override val description: String = "打招呼" override def execute (using command: InputCommand, event: Update): Unit = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_HELLO ).replyToMessageId(event.message.messageId) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala index ffaebfb..b9c2dfa 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInfoOnStart.scala @@ -4,21 +4,21 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML} import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendPhoto - -import scala.language.postfixOps +import com.pengrad.telegrambot.TelegramBot class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand { + private given TelegramBot = coeur.account override val name: String = "start" override val aliases: List[ICommandAlias] = Nil override def execute (using command: InputCommand, event: Update): Unit = { - coeur.account exec new SendPhoto( + SendPhoto( event.message.chat.id, getAboutPic ).caption( @@ -32,6 +32,7 @@ class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand { |(你可以随时通过 /info 重新获得这些信息)""" .stripMargin ).parseMode(ParseMode HTML) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala index 3fed1f8..fc0cc3a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyInformation.scala @@ -8,16 +8,16 @@ import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker} +import com.pengrad.telegrambot.TelegramBot import java.lang.System -import scala.language.postfixOps - // todo: maybe move some utils method outside class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account private case object Subs { val STICKERS = "stickers" @@ -43,7 +43,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { val action: String = command.args(0) action match { - case s if s startsWith Subs.STICKERS => echoStickers + case s if s `startsWith` Subs.STICKERS => echoStickers case Subs.RUNTIME => echoRuntime case Subs.VERSION | Subs.VERSION_2 => echoVersion case Subs.TASKS => echoTasksStatus @@ -54,7 +54,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { } private def echoInfo (chatId: Long, replyTo: Int): Unit = { - coeur.account exec new SendPhoto( + SendPhoto( chatId, getAboutPic ).caption( @@ -64,6 +64,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { |$getMornyAboutLinksHTML""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(replyTo) + .unsafeExecute } private def echoStickers (using command: InputCommand, event: Update): Unit = { @@ -73,8 +74,8 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { else if (command.args.length == 2) command.args(1) else null } else if (command.args.length == 1) { - if ((command.args(0) startsWith s"${Subs.STICKERS}.") || (command.args(0) startsWith s"${Subs.STICKERS}#")) { - command.args(0) substring Subs.STICKERS.length+1 + if ((command.args(0) `startsWith` s"${Subs.STICKERS}.") || (command.args(0) `startsWith` s"${Subs.STICKERS}#")) { + command.args(0) `substring` Subs.STICKERS.length+1 } else null } else null if (mid == null) echo404 @@ -87,7 +88,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { echoSticker(_key, _file_id) else { try { - val sticker = TelegramStickers getById mid + val sticker = TelegramStickers `getById` mid echoSticker(sticker.getKey, sticker.getValue) } catch case _: NoSuchFieldException => { echo404 @@ -99,15 +100,15 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { val send_mid = SendMessage(send_chat, mid) val send_sticker = SendSticker(send_chat, file_id) if (send_replyTo != -1) send_mid.replyToMessageId(send_replyTo) - val result_send_mid = coeur.account exec send_mid + val result_send_mid = send_mid.unsafeExecute send_sticker.replyToMessageId(result_send_mid.message.messageId) - coeur.account exec send_sticker + send_sticker.unsafeExecute } private[command] def echoVersion (using event: Update): Unit = { val versionDeltaHTML = MornySystem.VERSION_DELTA match {case Some(d) => s"-δ${h(d)}" case None => ""} val versionGitHTML = if (MornySystem.GIT_COMMIT nonEmpty) s"git $getVersionGitTagHTML" else "" - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, // language=html s"""version: @@ -120,11 +121,12 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { |- ${h(formatDate(MornySystem.CODE_TIMESTAMP, 0))} [UTC] |""".stripMargin ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML) + .unsafeExecute } private[command] def echoRuntime (using event: Update): Unit = { def sysprop (p: String): String = System.getProperty(p) - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, /* language=html */ s"""system: @@ -148,11 +150,12 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { |- [${coeur.coeurStartTimestamp}]""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) + .unsafeExecute } private def echoTasksStatus (using update: Update): Unit = { // if !coeur.trusted.isTrusted(update.message.from.id) then return; - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, // language=html s"""Coeur Task Scheduler: @@ -161,26 +164,29 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { | - current runner status: ${coeur.tasks.runnerState} |""".stripMargin ).parseMode(ParseMode.HTML).replyToMessageId(update.message.messageId) + .unsafeExecute } private def echoEventStatistics (using update: Update): Unit = { coeur.externalContext >> { (reporter: MornyReport) => - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, // language=html s"""Event Statistics : |in today |${reporter.EventStatistics.eventStatisticsHTML}""".stripMargin ).parseMode(ParseMode.HTML).replyToMessageId(update.message.messageId) + .unsafeExecute } || { echo404 } } private def echo404 (using event: Update): Unit = - coeur.account exec new SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyManagers.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyManagers.scala index 145ca37..e3592f8 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyManagers.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/command/MornyManagers.scala @@ -8,13 +8,13 @@ import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendSticker - -import scala.language.postfixOps +import com.pengrad.telegrambot.TelegramBot class MornyManagers (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account object Exit extends ITelegramCommand { @@ -27,22 +27,24 @@ class MornyManagers (using coeur: MornyCoeur) { val user = event.message.from - if (coeur.trusted isTrusted user.id) { + if (coeur.trusted isTrust user) { - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_EXIT ).replyToMessageId(event.message.messageId) - logger attention s"Morny exited by user ${user toLogTag}" + .unsafeExecute + logger `attention` s"Morny exited by user ${user toLogTag}" coeur.exit(0, user) } else { - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) - logger attention s"403 exit caught from user ${user toLogTag}" + .unsafeExecute + logger `attention` s"403 exit caught from user ${user toLogTag}" coeur.externalContext.consume[MornyReport](_.unauthenticatedAction("/exit", user)) } @@ -62,22 +64,24 @@ class MornyManagers (using coeur: MornyCoeur) { val user = event.message.from - if (coeur.trusted isTrusted user.id) { + if (coeur.trusted isTrust user) { - logger attention s"call save from command by ${user toLogTag}" + logger `attention` s"call save from command by ${user toLogTag}" coeur.saveDataAll() - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_SAVED ).replyToMessageId(event.message.messageId) + .unsafeExecute } else { - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) - logger attention s"403 save caught from user ${user toLogTag}" + .unsafeExecute + logger `attention` s"403 save caught from user ${user toLogTag}" coeur.externalContext.consume[MornyReport](_.unauthenticatedAction("/save", user)) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnInlineQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnInlineQuery.scala index 5d2dbc7..595715d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnInlineQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnInlineQuery.scala @@ -2,35 +2,38 @@ package cc.sukazyo.cono.morny.core.bot.event import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener, InlineQueryUnit, MornyQueryManager} -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.request.InlineQueryResult import com.pengrad.telegrambot.request.AnswerInlineQuery +import com.pengrad.telegrambot.TelegramBot import scala.collection.mutable.ListBuffer -import scala.language.postfixOps import scala.reflect.ClassTag class MornyOnInlineQuery (using queryManager: MornyQueryManager) (using coeur: MornyCoeur) extends EventListener { + private given TelegramBot = coeur.account override def onInlineQuery (using event: EventEnv): Unit = { import event.update - val results: List[InlineQueryUnit[_]] = queryManager query update + val results: List[InlineQueryUnit[?]] = queryManager `query` update var cacheTime = Int.MaxValue var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL - val resultAnswers = ListBuffer[InlineQueryResult[_]]() + val resultAnswers = ListBuffer[InlineQueryResult[?]]() for (r <- results) { if (cacheTime > r.cacheTime) cacheTime = r.cacheTime if (r isPersonal) isPersonal = true resultAnswers += r.result } - if (results isEmpty) return; + if (results isEmpty) return - coeur.account exec AnswerInlineQuery( - update.inlineQuery.id, resultAnswers toArray:_* + AnswerInlineQuery( + update.inlineQuery.id, + (resultAnswers toArray)* ).cacheTime(cacheTime).isPersonal(isPersonal) + .unsafeExecute event.setEventOk diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnTelegramCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnTelegramCommand.scala index c9b4eb7..bd4c4f0 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnTelegramCommand.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnTelegramCommand.scala @@ -14,21 +14,21 @@ class MornyOnTelegramCommand (using commandManager: MornyCommandManager) (using def _isCommandMessage(message: Message): Boolean = if message.text eq null then false - else if !(message.text startsWith "/") then false - else if message.text startsWith "/ " then false + else if !(message.text `startsWith` "/") then false + else if message.text `startsWith` "/ " then false else true if !_isCommandMessage(update.message) then return val inputCommand = InputCommand(update.message.text drop 1) givenCxt << inputCommand - logger trace ":provided InputCommand for event" + logger `trace` ":provided InputCommand for event" - if (!(inputCommand.command matches "^\\w+$")) - logger debug "not command" + if (!(inputCommand.command `matches` "^\\w+$")) + logger `debug` "not command" else if ((inputCommand.target ne null) && (inputCommand.target != coeur.username)) - logger debug "not morny command" + logger `debug` "not morny command" else - logger debug "is command" + logger `debug` "is command" if commandManager.execute(using inputCommand) then setEventOk diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala index eacdb3a..8111c3f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/bot/event/MornyOnUpdateTimestampOffsetLock.scala @@ -20,7 +20,7 @@ class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventLi override def on (using event: EventEnv): Unit = event.update.sourceTime match case Some(timestamp) => - if coeur.config.eventIgnoreOutdated && (EpochMillis.fromEpochSeconds(timestamp) < coeur.coeurStartTimestamp) then + if coeur.config.eventIgnoreOutdated && ((EpochMillis fromSeconds timestamp) < coeur.coeurStartTimestamp) then event.setEventCanceled case None => diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpService4Api.scala b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpService4Api.scala index 7cd2069..4368e19 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpService4Api.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/HttpService4Api.scala @@ -1,7 +1,7 @@ package cc.sukazyo.cono.morny.core.http.api import cats.effect.IO -import cc.sukazyo.cono.morny.core.Log.exceptionLog +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import org.http4s.{HttpRoutes, Response} trait HttpService4Api { @@ -13,7 +13,7 @@ trait HttpService4Api { response.setMornyInternalErrorHeader( e.getClass.getSimpleName, e.getMessage, - exceptionLog(e) + e.toLogString ) def setMornyInternalErrorHeader ( `Morny-Internal-Error-Type`: String, diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/MornyHttpServerContext.scala b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/MornyHttpServerContext.scala index 50ad0d5..604803c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/http/api/MornyHttpServerContext.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/http/api/MornyHttpServerContext.scala @@ -2,6 +2,8 @@ package cc.sukazyo.cono.morny.core.http.api trait MornyHttpServerContext { + infix def register4API (service: HttpService4Api): Unit + def register4API (service: HttpService4Api*): Unit def start: HttpServer diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/http/internal/MornyHttpServerContextImpl.scala b/src/main/scala/cc/sukazyo/cono/morny/core/http/internal/MornyHttpServerContextImpl.scala index e637963..f4288eb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/http/internal/MornyHttpServerContextImpl.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/http/internal/MornyHttpServerContextImpl.scala @@ -3,7 +3,8 @@ package cc.sukazyo.cono.morny.core.http.internal import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.http.api.{HttpServer, HttpService4Api, MornyHttpServerContext} import cc.sukazyo.cono.morny.core.http.ServiceUI -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import scala.collection.mutable @@ -13,6 +14,9 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ private lazy val service_ui = ServiceUI() + override infix def register4API (service: HttpService4Api): Unit = + services_api += service + override def register4API (services: HttpService4Api*): Unit = services_api ++= services @@ -31,9 +35,9 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ def errorHandler (t: Throwable, message: =>String): OptionT[IO, Unit] = OptionT.liftF(IO { - logger error + logger `error` s"""Unexpected exception occurred on Morny Http Server : - |${exceptionLog(t)}""".stripMargin + |${t.toLogString}""".stripMargin }) val withErrorHandler = ErrorHandling.Recover.total( ErrorAction.log( @@ -49,7 +53,7 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ .resource val (_server, _shutdown_io) = server.allocated.unsafeRunSync() match case (_1, _2) => (_1, _2) - logger notice s"Morny HTTP Server started at ${_server.baseUri}" + logger `notice` s"Morny HTTP Server started at ${_server.baseUri}" new HttpServer(using global): val server: Server = _server diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyFormatterConsole.scala b/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyFormatterConsole.scala index 4f13086..b52b210 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyFormatterConsole.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyFormatterConsole.scala @@ -5,9 +5,6 @@ import cc.sukazyo.cono.morny.util.CommonFormat.formatDate import cc.sukazyo.messiva.formatter.ILogFormatter import cc.sukazyo.messiva.log.Log -import java.time.{ZoneId, ZoneOffset} -import java.util.TimeZone - class MornyFormatterConsole extends ILogFormatter { override def format (log: Log): String = diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyLoggerBase.scala b/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyLoggerBase.scala index 62cc15a..472a313 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyLoggerBase.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/internal/logging/MornyLoggerBase.scala @@ -8,7 +8,7 @@ class MornyLoggerBase extends Logger with IMornyLogLevelImpl { def this (appends: IAppender*) = this() - this.appends.addAll(java.util.List.of(appends:_*)) + this.appends.addAll(java.util.List.of(appends*)) override def notice (message: String): Unit = pushToAllAppender(Log(1, new Message(message), MornyLogLevels.NOTICE)) 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 b94d554..2e14d95 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala @@ -13,7 +13,7 @@ object MornyInformation { case None => "" case Some(commit) => val g = StringBuilder() - val cm = commit substring(0, 8) + val cm = commit.substring(0, 8) val cp = MornySystem.currentCodePath if (cp == null) g ++= s"$cm" else g ++= s"$cm" 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 d642e1c..b3a1081 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala @@ -20,7 +20,7 @@ object TelegramImages { @throws[AssetsException] private def read (): Unit = { - Using ((MornyAssets.pack getResource assetsPath)read) { stream => + Using ((MornyAssets.pack `getResource` assetsPath)read) { stream => try { this.cache = Some(stream.readAllBytes()) } catch case e: IOException => { throw AssetsException(e) diff --git a/src/main/scala/cc/sukazyo/cono/morny/encrypt_tool/Encryptor.scala b/src/main/scala/cc/sukazyo/cono/morny/encrypt_tool/Encryptor.scala index a86538c..695f6c1 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/encrypt_tool/Encryptor.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/encrypt_tool/Encryptor.scala @@ -10,18 +10,20 @@ import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.CommonEncrypt import cc.sukazyo.cono.morny.util.CommonEncrypt.* import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{PhotoSize, Update} import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{GetFile, SendDocument, SendMessage, SendSticker} +import com.pengrad.telegrambot.TelegramBot import java.io.IOException import java.net.{URLDecoder, URLEncoder} import java.util.Base64 -import scala.language.postfixOps /** Provides Telegram Command __`/encrypt`__. */ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account override val name: String = "encrypt" override val aliases: List[ICommandAlias] = ListedAlias("enc") :: Nil @@ -33,7 +35,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { val args = command.args // show a simple help page - if ((args isEmpty) || ((args(0) equals "l") && (args.length == 1))) + if ((args isEmpty) || ((args(0) `equals` "l") && (args.length == 1))) echoHelp(event.message.chat.id, event.message.messageId) return @@ -45,17 +47,18 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { // so the algorithm will be and must be in the 2nd arg. /** inner function: is input `arg` means mod-param ''uppercase'' */ def _is_mod_u(arg: String): Boolean = - if (arg equalsIgnoreCase "uppercase") return true - if (arg equalsIgnoreCase "u") return true - if (arg equalsIgnoreCase "upper") return true + if (arg `equalsIgnoreCase` "uppercase") return true + if (arg `equalsIgnoreCase` "u") return true + if (arg `equalsIgnoreCase` "upper") return true false val mod_uppercase = if (args.length > 1) { if (args.length < 3 && _is_mod_u(args(1))) true else - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) + .unsafeExecute return } else false @@ -73,15 +76,16 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { val asByteArray: Array[Byte] = data /** inner class: the [[XEncryptable]] implementation of [[String]] data */ case class XText (data: String) extends XEncryptable: - val asByteArray: Array[Byte] = data getBytes CommonEncrypt.ENCRYPT_STANDARD_CHARSET + val asByteArray: Array[Byte] = data.getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET) val input: XEncryptable = val _r = event.message.replyToMessage if ((_r ne null) && (_r.document ne null)) { try {XFile( - coeur.account getFileContent (coeur.account exec GetFile(_r.document.fileId)).file, + GetFile(_r.document.fileId).unsafeExecute + .file.getContent, _r.document.fileName )} catch case e: IOException => - logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" + logger `warn` s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" coeur.externalContext.consume[MornyReport](_.exception(e, "NetworkRequest error: TelegramFileAPI")) return } else if ((_r ne null) && (_r.photo ne null)) { @@ -96,26 +100,28 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.") import cc.sukazyo.cono.morny.util.UseRandom.rand_id XFile( - coeur.account getFileContent (coeur.account exec GetFile(_photo_origin.fileId)).file, + GetFile(_photo_origin.fileId).unsafeExecute + .file.getContent, s"photo$rand_id.png" ) } catch case e: IOException => //noinspection DuplicatedCode - logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" + logger `warn` s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" coeur.externalContext.consume[MornyReport](_.exception(e, "NetworkRequest error: TelegramFileAPI")) return case e: IllegalArgumentException => - logger warn s"FileProcess error: PhotoSize:\n\t${e.getMessage}" + logger `warn` s"FileProcess error: PhotoSize:\n\t${e.getMessage}" coeur.externalContext.consume[MornyReport](_.exception(e, "FileProcess error: PhotoSize")) return } else if ((_r ne null) && (_r.text ne null)) { XText(_r.text) } else { - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, "null" ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) + .unsafeExecute return } // END BLOCK: get input @@ -141,14 +147,15 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { EXHash(if mod_uppercase then hashed toUpperCase else hashed) //noinspection UnitMethodIsParameterless def echo_unsupported: Unit = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) + .unsafeExecute val result: EXHash|EXFile|EXText = args(0) match case "base64" | "b64" | "base64url" | "base64u" | "b64u" => val _tool_b64 = - if args(0) contains "u" then Base64.getUrlEncoder + if args(0) `contains` "u" then Base64.getUrlEncoder else Base64.getEncoder genResult_encrypt( input, @@ -157,7 +164,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { ) case "base64decode" | "base64d" | "b64d" | "base64url-decode" | "base64ud" | "b64ud" => val _tool_b64d = - if args(0) contains "u" then Base64.getUrlDecoder + if args(0) `contains` "u" then Base64.getUrlDecoder else Base64.getDecoder try { genResult_encrypt( input, @@ -190,17 +197,19 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { // output result match case _file: EXFile => - coeur.account exec SendDocument( + SendDocument( event.message.chat.id, _file.result ).fileName(_file.resultName).replyToMessageId(event.message.messageId) + .unsafeExecute case _text: EXTextLike => import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, // language=html s"
${h(_text.text)}
" ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) + .unsafeExecute } @@ -231,7 +240,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { * */ private def echoHelp(chat: Long, replyTo: Int): Unit = - coeur.account exec SendMessage( + SendMessage( chat, s"""base64, b64 |base64url, base64u, b64u @@ -247,5 +256,6 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { |uppercase, upper, u (sha1/sha256/sha512/md5 only)""" .stripMargin ).replyToMessageId(replyTo).parseMode(ParseMode HTML) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/ip186/BotCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/ip186/BotCommand.scala index c4d94dd..bc198f9 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/ip186/BotCommand.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/ip186/BotCommand.scala @@ -3,14 +3,16 @@ package cc.sukazyo.cono.morny.ip186 import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class BotCommand (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account private enum Subs (val cmd: String): case IP extends Subs("ip") @@ -35,18 +37,20 @@ class BotCommand (using coeur: MornyCoeur) { if (command.args isEmpty) if event.message.replyToMessage eq null then null else event.message.replyToMessage.text else if (command.args.length > 1) - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, "[Unavailable] Too much arguments." ).replyToMessageId(event.message.messageId) + .unsafeExecute return else command.args(0) if (target eq null) - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, "[Unavailable] No ip defined." ).replyToMessageId(event.message.messageId) + .unsafeExecute return; @@ -58,20 +62,22 @@ class BotCommand (using coeur: MornyCoeur) { case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target) case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}") - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, s"""${h(response.url)} |${h(response.body)}""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) + .unsafeExecute } catch case e: Exception => - coeur.account exec new SendMessage( + SendMessage( event.message().chat().id(), s"""[Exception] in query: |${h(e.getMessage)}""" .stripMargin ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/ip186/IP186QueryHandler.scala b/src/main/scala/cc/sukazyo/cono/morny/ip186/IP186QueryHandler.scala index 1685de3..a915ad3 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/ip186/IP186QueryHandler.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/ip186/IP186QueryHandler.scala @@ -28,7 +28,7 @@ object IP186QueryHandler { @throws[IOException] def query_whoisPretty (domain: String): IP186Response = val raw = query_whois(domain) - IP186Response(raw.url, raw.body substring(0, (raw.body indexOf "<<<")+3)) + IP186Response(raw.url, raw.body `substring`(0, (raw.body `indexOf` "<<<")+3)) @throws[IOException] private def commonQuery (requestPath: Uri): IP186Response = { diff --git a/src/main/scala/cc/sukazyo/cono/morny/medication_timer/MedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/medication_timer/MedicationTimer.scala index 80489f2..0d809b0 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/medication_timer/MedicationTimer.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/medication_timer/MedicationTimer.scala @@ -4,7 +4,7 @@ import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.medication_timer.MedicationTimer.calcNextRoutineTimestamp import cc.sukazyo.cono.morny.util.schedule.RoutineTask -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import cc.sukazyo.cono.morny.util.CommonFormat import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis import com.cronutils.builder.CronBuilder @@ -13,12 +13,14 @@ import com.cronutils.model.time.ExecutionTime import com.pengrad.telegrambot.model.{Message, MessageEntity} import com.pengrad.telegrambot.request.{EditMessageText, SendMessage} import com.pengrad.telegrambot.response.SendResponse +import com.pengrad.telegrambot.TelegramBot import java.time.{Instant, ZonedDateTime, ZoneOffset} import scala.collection.mutable.ArrayBuffer import scala.language.implicitConversions class MedicationTimer (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account private val NOTIFY_MESSAGE = "🍥⏲" private val DAEMON_THREAD_NAME_DEF = "MedicationTimer" @@ -36,7 +38,7 @@ class MedicationTimer (using coeur: MornyCoeur) { def calcNextSendTime: EpochMillis = val next_time = calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour) - logger info s"medication timer will send next notify at ${CommonFormat.formatDate(next_time, use_timeZone.getTotalSeconds / 60 / 60)} with $use_timeZone [$next_time]" + logger `info` s"medication timer will send next notify at ${CommonFormat.formatDate(next_time, use_timeZone.getTotalSeconds / 60 / 60)} with $use_timeZone [$next_time]" next_time override def firstRoutineTimeMillis: EpochMillis = @@ -47,24 +49,24 @@ class MedicationTimer (using coeur: MornyCoeur) { override def main: Unit = { sendNotification() - logger info "medication notify sent." + logger `info` "medication notify sent." } } def start(): Unit = if ((notify_toChat == -1) || (notify_atHour isEmpty)) - logger notice "Medication Timer disabled : related param is not complete set" + logger `notice` "Medication Timer disabled : related param is not complete set" return; coeur.tasks ++ scheduleTask - logger notice "Medication Timer started." + logger `notice` "Medication Timer started." def stop(): Unit = coeur.tasks % scheduleTask - logger notice "Medication Timer stopped." + logger `notice` "Medication Timer stopped." private def sendNotification(): Unit = { - val sendResponse: SendResponse = coeur.account exec SendMessage(notify_toChat, NOTIFY_MESSAGE) + val sendResponse: SendResponse = SendMessage(notify_toChat, NOTIFY_MESSAGE).unsafeExecute if sendResponse isOk then lastNotify_messageId = Some(sendResponse.message.messageId) else lastNotify_messageId = None } @@ -76,11 +78,11 @@ class MedicationTimer (using coeur: MornyCoeur) { val entities = ArrayBuffer.empty[MessageEntity] if edited.entities ne null then entities ++= edited.entities entities += MessageEntity(MessageEntity.Type.italic, edited.text.length + "\n-- ".length, editTime.length) - coeur.account exec EditMessageText( + EditMessageText( notify_toChat, edited.messageId, edited.text + s"\n-- $editTime --" - ).entities(entities toArray:_*) + ).entities((entities toArray)*).unsafeExecute lastNotify_messageId = None true } @@ -105,7 +107,7 @@ object MedicationTimer { })) .instance ).nextExecution( - ZonedDateTime ofInstant (Instant ofEpochMilli baseTimeMillis, zone.normalized) + ZonedDateTime `ofInstant` (Instant `ofEpochMilli` baseTimeMillis, zone.normalized) ).get.toInstant.toEpochMilli } diff --git a/src/main/scala/cc/sukazyo/cono/morny/medication_timer/ModuleMedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/medication_timer/ModuleMedicationTimer.scala index 15f2dad..a900275 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/medication_timer/ModuleMedicationTimer.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/medication_timer/ModuleMedicationTimer.scala @@ -27,7 +27,7 @@ class ModuleMedicationTimer extends MornyInternalModule { externalContext >> { (instance: MedicationTimer) => eventManager register OnMedicationNotifyApply(using instance) } || { - logger warn "There seems no Medication Timer instance is provided; skipped register events for it." + logger `warn` "There seems no Medication Timer instance is provided; skipped register events for it." } } @@ -38,7 +38,7 @@ class ModuleMedicationTimer extends MornyInternalModule { externalContext >> { (instance: MedicationTimer) => instance.start() } || { - logger warn "There seems no Medication Timer instance is provided; skipped start it." + logger `warn` "There seems no Medication Timer instance is provided; skipped start it." } } @@ -49,7 +49,7 @@ class ModuleMedicationTimer extends MornyInternalModule { externalContext >> { (instance: MedicationTimer) => instance.stop() } || { - logger warn "There seems no Medication Timer instance need to be stop." + logger `warn` "There seems no Medication Timer instance need to be stop." } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyJrrp.scala b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyJrrp.scala index 9be67bc..53e5d84 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyJrrp.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyJrrp.scala @@ -8,7 +8,7 @@ import scala.language.postfixOps object MornyJrrp { def jrrp_of_telegramUser (user: User, timestamp: EpochMillis): Double = - jrrp_v_xmomi(user.id, EpochDays fromEpochMillis timestamp) * 100.0 + jrrp_v_xmomi(user.id, EpochDays fromMillis timestamp) * 100.0 private def jrrp_v_xmomi (identifier: Long, dayStamp: EpochDays): Double = import cc.sukazyo.cono.morny.util.CommonEncrypt.MD5 diff --git a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyOldJrrp.scala b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyOldJrrp.scala index 16f9ed0..1ba5e7d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyOldJrrp.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/MornyOldJrrp.scala @@ -4,12 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account override val name: String = "jrrp" override val aliases: List[ICommandAlias] = Nil @@ -26,11 +28,12 @@ class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand { case _ => "..." import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, // language=html f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— $jrrp%.2f%% ${h(ending)}" ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala index 1401039..66ad141 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/morny_misc/Testing.scala @@ -3,25 +3,28 @@ package cc.sukazyo.cono.morny.morny_misc import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class Testing (using coeur: MornyCoeur) extends ISimpleCommand { + private given TelegramBot = coeur.account override val name: String = "test" override val aliases: List[ICommandAlias] = Nil override def execute (using command: InputCommand, event: Update): Unit = { - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, // language=html "Just a TEST command." ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/CommandNbnhhsh.scala b/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/CommandNbnhhsh.scala index 10c1e23..524ba12 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/CommandNbnhhsh.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/CommandNbnhhsh.scala @@ -6,15 +6,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendSticker} +import com.pengrad.telegrambot.TelegramBot import sttp.client3.{HttpError, SttpClientException} -import scala.language.postfixOps - class CommandNbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account private val NBNHHSH_RESULT_HEAD_HTML = // language=html @@ -33,52 +33,54 @@ class CommandNbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand { else if (event.message.replyToMessage != null && event.message.replyToMessage.text != null) event.message.replyToMessage.text else - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) + .unsafeExecute return; try { - val queryResp = NbnhhshQuery sendGuess queryTarget + val queryResp = NbnhhshQuery `sendGuess` queryTarget val message = StringBuilder(NBNHHSH_RESULT_HEAD_HTML) - logger trace s"**nbnhhsh got len=${queryResp.words.length}" + logger `trace` s"**nbnhhsh got len=${queryResp.words.length}" for (_word <- queryResp.words) { - logger trace s"**start for ${_word.name}" + logger `trace` s"**start for ${_word.name}" val _use_trans = (_word.trans ne null) && (_word.trans nonEmpty) val _use_inputting = (_word.inputting ne null) && (_word.inputting nonEmpty) if (_use_trans || _use_inputting) message ++= s"\n\n[[ ${h(_word.name)} ]]" - logger trace s"**used [${_word.name}]" + logger `trace` s"**used [${_word.name}]" if (_use_trans) for (_trans <- _word.trans) message ++= s"\n* ${h(_trans)}" - logger trace s"**used [${_word.name}] used `${_trans}``" + logger `trace` s"**used [${_word.name}] used `${_trans}``" if (_use_inputting) - logger trace s"**used [${_word.name}] inputting" + logger `trace` s"**used [${_word.name}] inputting" if (_use_trans) message += '\n' message ++= " maybe:" for (_inputting <- _word.inputting) - logger trace s"**used [${_word.name}] used-i ${_inputting}" + logger `trace` s"**used [${_word.name}] used-i ${_inputting}" message ++= s"\n` ${h(_inputting)}" - logger trace s"**done" + logger `trace` s"**done" } - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, message toString ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) + .unsafeExecute } catch case e: (HttpError[_] | SttpClientException) => { - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, s"""[Exception] in query: |${h(e.getMessage)} |""".stripMargin - ) + ).unsafeExecute } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/NbnhhshQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/NbnhhshQuery.scala index 0bd6ced..03b092c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/NbnhhshQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/nbnhhsh/NbnhhshQuery.scala @@ -20,7 +20,7 @@ object NbnhhshQuery { private val httpClient = OkHttpSyncBackend() - @throws[HttpError[_]|SttpClientException] + @throws[HttpError[?]|SttpClientException] def sendGuess (text: String): GuessResult = { case class GuessRequest (text: String) val http = mornyBasicRequest diff --git a/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnQuestionMarkReply.scala b/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnQuestionMarkReply.scala index a3ea6e0..793f942 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnQuestionMarkReply.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnQuestionMarkReply.scala @@ -3,13 +3,15 @@ package cc.sukazyo.cono.morny.randomize_somthing import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.randomize_somthing.OnQuestionMarkReply.isAllMessageMark -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot -import scala.language.postfixOps import scala.util.boundary +import scala.util.boundary.break class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener { + private given TelegramBot = coeur.account override def onMessage (using event: EventEnv): Unit = { import event.update @@ -21,9 +23,10 @@ class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener { if (1 over 8) chance_is false then return; if !isAllMessageMark(using update.message.text) then return; - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, update.message.text ).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } @@ -40,7 +43,7 @@ object OnQuestionMarkReply { boundary[Boolean] { for (c <- text) if !(QUESTION_MARKS contains c) then - boundary break false + break(false) true } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnUserRandom.scala b/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnUserRandom.scala index d7f2989..243cecb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnUserRandom.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/randomize_somthing/OnUserRandom.scala @@ -2,13 +2,14 @@ package cc.sukazyo.cono.morny.randomize_somthing import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.request.SendMessage -import com.pengrad.telegrambot.response.SendResponse +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class OnUserRandom (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account object RandomSelect extends EventListener { @@ -19,10 +20,10 @@ class OnUserRandom (using coeur: MornyCoeur) { import event.update if update.message.text == null then return; - if !(update.message.text startsWith "/") then return + if !(update.message.text `startsWith` "/") then return import cc.sukazyo.cono.morny.util.UseRandom.rand_half - val query = update.message.text substring 1 + val query = update.message.text `substring` 1 val result: String | Null = query match case USER_OR_QUERY(_con1, _con2) => if rand_half then _con1 else _con2 @@ -34,10 +35,11 @@ class OnUserRandom (using coeur: MornyCoeur) { if result == null then return; - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, result ).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } @@ -58,14 +60,15 @@ class OnUserRandom (using coeur: MornyCoeur) { var result: String|Null = null import cc.sukazyo.cono.morny.util.UseRandom.rand_half for (k <- keywords) - if update.message.text endsWith k then + if update.message.text `endsWith` k then result = if rand_half then "尊嘟" else "假嘟" - if result == null then return; + if result == null then return - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, result ).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } diff --git a/src/main/scala/cc/sukazyo/cono/morny/reporter/Module.scala b/src/main/scala/cc/sukazyo/cono/morny/reporter/Module.scala index 2450024..e99c771 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/reporter/Module.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/reporter/Module.scala @@ -27,10 +27,10 @@ class Module extends MornyInternalModule { import cxt.* externalContext >> { (instance: MornyReport) => - logger info "MornyReport will now collect your bot event statistics." + logger `info` "MornyReport will now collect your bot event statistics." eventManager register instance.EventStatistics.EventInfoCatcher } || { - logger warn "There seems no reporter instance is provided; skipped register events for it." + logger `warn` "There seems no reporter instance is provided; skipped register events for it." } } @@ -40,7 +40,7 @@ class Module extends MornyInternalModule { externalContext >> { (instance: MornyReport) => instance.start() } || { - logger warn "There seems no reporter instance is provided; skipped start it." + logger `warn` "There seems no reporter instance is provided; skipped start it." } } @@ -56,7 +56,7 @@ class Module extends MornyInternalModule { externalContext >> { (instance: MornyReport) => instance.stop() } || { - logger warn "There seems no reporter instance need to be stop." + logger `warn` "There seems no reporter instance need to be stop." } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala b/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala index 74a0c4f..fe5a40b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/reporter/MornyReport.scala @@ -1,19 +1,20 @@ package cc.sukazyo.cono.morny.reporter import cc.sukazyo.cono.morny.core.{MornyCoeur, MornyConfig} -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.data.MornyInformation.getVersionAllFullTagHTML import cc.sukazyo.cono.morny.util.statistics.{NumericStatistics, UniqueCounter} import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import cc.sukazyo.cono.morny.util.EpochDateTime.DurationMillis import cc.sukazyo.cono.morny.util.schedule.CronTask import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Update.{sourceChat, sourceUser} import cc.sukazyo.cono.morny.util.CommonEncrypt.hashId import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import com.cronutils.builder.CronBuilder import com.cronutils.model.Cron import com.cronutils.model.definition.CronDefinitionBuilder @@ -30,25 +31,25 @@ class MornyReport (using coeur: MornyCoeur) { private val enabled = coeur.config.reportToChat != -1 if !enabled then - logger info "Morny Report is disabled : report chat is set to -1" + logger `info` "Morny Report is disabled : report chat is set to -1" private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = { if !enabled then return try { - coeur.account exec report + report.unsafeExecute(using coeur.account) } catch case e: EventRuntimeException => { import EventRuntimeException.* e match case e: ActionFailed => - logger warn + logger `warn` s"""cannot execute report to telegram: - |${exceptionLog(e) indent 4} + |${e.toLogString `indent` 4} | tg-api response: - |${(e.response toString) indent 4}""".stripMargin + |${(e.response toString) `indent` 4}""".stripMargin case e: ClientFailed => - logger error + logger `error` s"""failed when report to telegram: - |${exceptionLog(e.getCause) indent 4} + |${e.getCause.toLogString `indent` 4} |""".stripMargin } } @@ -69,7 +70,7 @@ class MornyReport (using coeur: MornyCoeur) { // language=html s"""▌Coeur Unexpected Exception |${if description ne null then h(description)+"\n" else ""} - |
${h(exceptionLog(e))}
$_tgErrFormat""" + |
${h(e.toLogString)}
$_tgErrFormat""" .stripMargin ).parseMode(ParseMode HTML)) } @@ -120,9 +121,9 @@ class MornyReport (using coeur: MornyCoeur) { case e: (IllegalAccessException|IllegalArgumentException|NullPointerException) => // language=html echo ++= s": ${h("")}" - logger error + logger `error` s"""error while reading config field ${field.getName} - |${exceptionLog(e)}""".stripMargin + |${e.toLogString}""".stripMargin exception(e, s"error while reading config field ${field.getName}") echo ++= "\n" } @@ -224,14 +225,14 @@ class MornyReport (using coeur: MornyCoeur) { case State.OK(from) => val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup) givenCxt << timeUsed - logger debug + logger `debug` s"""event done with OK | with time consumed ${timeUsed.it}ms | by $from""".stripMargin runningTime ++ timeUsed.it case State.CANCELED(from) => eventCanceled += 1 - logger debug + logger `debug` s"""event done with CANCELED" | by $from""".stripMargin case null => diff --git a/src/main/scala/cc/sukazyo/cono/morny/slash_action/OnUserSlashAction.scala b/src/main/scala/cc/sukazyo/cono/morny/slash_action/OnUserSlashAction.scala index 663ffda..20d9a7e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/slash_action/OnUserSlashAction.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/slash_action/OnUserSlashAction.scala @@ -5,14 +5,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.UniversalCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec -import com.pengrad.telegrambot.model.Update +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { + private given TelegramBot = coeur.account private val TG_FORMAT = "^\\w+(@\\w+)?$"r @@ -22,7 +23,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { val text = update.message.text if text == null then return; - if (text startsWith "/") { + if (text `startsWith` "/") { // there has to be some special conditions for DP7 // due to I have left DP7, I closed those special @@ -33,7 +34,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { // these message, here to remember the old DP7. val actions = UniversalCommand.Lossy(text) - actions(0) = actions(0) substring 1 + actions(0) = actions(0) `substring` 1 actions(0) @@ -42,7 +43,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { case TG_FORMAT(_) => return; // ignore Path link - case x if x contains "/" => return; + case x if x `contains` "/" => return; case _ => val isHardParse = actions(0) isBlank @@ -52,7 +53,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { val hasObject = actions.length != hp_len(1) val v_object = if hasObject then - actions slice(hp_len(1), actions.length) mkString " " + actions `slice` (hp_len(1), actions.length) `mkString` " " else "" val origin = update.message val target = @@ -60,7 +61,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { origin else update.message.replyToMessage - coeur.account exec SendMessage( + SendMessage( update.message.chat.id, "%s %s%s %s %s!".format( origin.sender_firstnameRefHTML, @@ -71,6 +72,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { if hasObject then h(v_object+" ") else "" ) ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) + .unsafeExecute event.setEventOk } diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/ModuleSocialShare.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/ModuleSocialShare.scala index 62aab45..efff7bf 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/ModuleSocialShare.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/ModuleSocialShare.scala @@ -25,4 +25,3 @@ class ModuleSocialShare extends MornyInternalModule { } } - \ No newline at end of file diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/api/SocialContent.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/api/SocialContent.scala index 84c5163..89106a6 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/api/SocialContent.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/api/SocialContent.scala @@ -4,10 +4,11 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.InlineQueryUnit import cc.sukazyo.cono.morny.social_share.api.SocialContent.{SocialMedia, SocialMediaType, SocialMediaWithUrl} import cc.sukazyo.cono.morny.social_share.api.SocialContent.SocialMediaType.{Photo, Video} -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId import com.pengrad.telegrambot.model.request.* import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage} +import com.pengrad.telegrambot.TelegramBot /** Model of social networks' status. for example twitter tweet or * weibo status. @@ -44,18 +45,19 @@ case class SocialContent ( case _ => orElse def outputToTelegram (using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Unit = { + given TelegramBot = coeur.account if medias isEmpty then - coeur.account exec - SendMessage(replyChat, text_html) - .parseMode(ParseMode.HTML) - .replyToMessageId(replyToMessage) + SendMessage(replyChat, text_html) + .parseMode(ParseMode.HTML) + .replyToMessageId(replyToMessage) + .unsafeExecute else val mediaGroup = medias.map(f => f.genTelegramInputMedia) mediaGroup.head.caption(text_html) mediaGroup.head.parseMode(ParseMode.HTML) - coeur.account exec - SendMediaGroup(replyChat, mediaGroup: _*) - .replyToMessageId(replyToMessage) + SendMediaGroup(replyChat, mediaGroup*) + .replyToMessageId(replyToMessage) + .unsafeExecute } def genInlineQueryResults (using id_head: String, id_param: Any, name: String): List[InlineQueryUnit[?]] = { @@ -99,13 +101,13 @@ object SocialContent { def genTelegramInputMedia: InputMedia[?] } case class SocialMediaWithUrl (url: String)(t: SocialMediaType) extends SocialMedia(t) { - override def genTelegramInputMedia: InputMedia[_] = + override def genTelegramInputMedia: InputMedia[?] = t match case Photo => InputMediaPhoto(url) case Video => InputMediaVideo(url) } case class SocialMediaWithBytesData (data: Array[Byte])(t: SocialMediaType) extends SocialMedia(t) { - override def genTelegramInputMedia: InputMedia[_] = + override def genTelegramInputMedia: InputMedia[?] = t match case Photo => InputMediaPhoto(data) case Video => InputMediaVideo(data) diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/command/GetSocial.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/command/GetSocial.scala index 0b9ab8c..34afa39 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/command/GetSocial.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/command/GetSocial.scala @@ -5,11 +5,13 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.social_share.event.OnGetSocial import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendSticker +import com.pengrad.telegrambot.TelegramBot class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account override val name: String = "get" override val aliases: List[ICommandAlias] = Nil @@ -19,10 +21,11 @@ class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand { override def execute (using command: InputCommand, event: Update): Unit = { def do404 (): Unit = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers.ID_404 ).replyToMessageId(event.message.messageId()) + .unsafeExecute val content = if command.args.length > 0 then diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/event/OnGetSocial.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/event/OnGetSocial.scala index 38f0bc3..9f44841 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/event/OnGetSocial.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/event/OnGetSocial.scala @@ -1,6 +1,6 @@ package cc.sukazyo.cono.morny.social_share.event -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.data.TelegramStickers @@ -8,11 +8,13 @@ import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.social_share.api.{SocialTwitterParser, SocialWeiboParser} import cc.sukazyo.cono.morny.social_share.event.OnGetSocial.tryFetchSocial import cc.sukazyo.cono.morny.social_share.external.{twitter, weibo} -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Message.textWithUrls +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import com.pengrad.telegrambot.model.Chat import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendSticker} +import com.pengrad.telegrambot.TelegramBot class OnGetSocial (using coeur: MornyCoeur) extends EventListener { @@ -80,36 +82,39 @@ object OnGetSocial { val api = FXApi.Fetch.status(Some(url.screenName), url.statusId) SocialTwitterParser.parseFXTweet(api).outputToTelegram } catch case e: (SttpClientException | ParsingFailure | DecodingFailure) => - coeur.account exec SendSticker( + SendSticker( replyChat, TelegramStickers.ID_NETWORK_ERR - ).replyToMessageId(replyToMessage) - logger error - "Error on requesting FixTweet API\n" + exceptionLog(e) + ).replyToMessageId(replyToMessage).unsafeExecute(using coeur.account) + logger `error` + "Error on requesting FixTweet API\n" + e.toLogString coeur.externalContext.consume[MornyReport](_.exception(e, "Error on requesting FixTweet API")) def tryFetchSocialOfWeibo (url: weibo.StatusUrlInfo)(using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Unit = import cc.sukazyo.cono.morny.social_share.external.weibo.MApi import io.circe.{DecodingFailure, ParsingFailure} import sttp.client3.{HttpError, SttpClientException} + given TelegramBot = coeur.account try { val api = MApi.Fetch.statuses_show(url.id) SocialWeiboParser.parseMStatus(api).outputToTelegram } catch case e: HttpError[?] => - coeur.account exec SendMessage( + SendMessage( replyChat, // language=html s"""Weibo Request Error ${e.statusCode} |
${e.body}
""".stripMargin ).replyToMessageId(replyToMessage).parseMode(ParseMode.HTML) + .unsafeExecute case e: (SttpClientException | ParsingFailure | DecodingFailure) => - coeur.account exec SendSticker( + SendSticker( replyChat, TelegramStickers.ID_NETWORK_ERR ).replyToMessageId(replyToMessage) - logger error - "Error on requesting Weibo m.API\n" + exceptionLog(e) + .unsafeExecute + logger `error` + "Error on requesting Weibo m.API\n" + e.toLogString coeur.externalContext.consume[MornyReport](_.exception(e, "Error on requesting Weibo m.API")) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BiliTool.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BiliTool.scala index 1fd831f..01caad7 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BiliTool.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BiliTool.scala @@ -108,12 +108,12 @@ object BiliTool { */ def toBv (av: Long): String = { val _av = (av^V_CONV_XOR)+V_CONV_ADD - val bv = Array(BV_TEMPLATE:_*) + val bv = Array(BV_TEMPLATE*) for (i <- BV_TEMPLATE_FILTER.indices) { import Math.{floor, pow} bv(BV_TEMPLATE_FILTER(i)) = BV_TABLE( (floor(_av/(TABLE_INT**i)) % TABLE_INT) toInt ) } - String copyValueOf bv + String `copyValueOf` bv } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BilibiliForms.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BilibiliForms.scala index d6387bb..c62b35c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BilibiliForms.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/bilibili/BilibiliForms.scala @@ -72,7 +72,7 @@ object BilibiliForms { IllegalArgumentException(s"not a b23.tv url: $uri") else if uri.pathSegments.segments.size < 1 then throw IllegalArgumentException(s"empty b23.tv url: $uri") - else if uri.pathSegments.segments.head.v matches REGEX_BILI_ID.regex then + else if uri.pathSegments.segments.head.v `matches` REGEX_BILI_ID.regex then throw IllegalArgumentException(s"is a b23 video link: $uri . (use parse_videoUrl instead)") try { diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/weibo/MApi.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/weibo/MApi.scala index bc0e373..01bf176 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/external/weibo/MApi.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/external/weibo/MApi.scala @@ -33,7 +33,7 @@ object MApi { private val httpClient = OkHttpSyncBackend() - @throws[HttpError[_]|SttpClientException|ParsingFailure|DecodingFailure] + @throws[HttpError[?]|SttpClientException|ParsingFailure|DecodingFailure] def statuses_show (id: String): MApi[MStatus] = import sttp.client3.asString import MApi.CirceADTs.given @@ -46,7 +46,7 @@ object MApi { .as[MApi[MStatus]] .toTry.get - @throws[HttpError[_] | SttpClientException | ParsingFailure | DecodingFailure] + @throws[HttpError[?] | SttpClientException | ParsingFailure | DecodingFailure] def pic (picUrl: String): Array[Byte] = import sttp.client3.* import sttp.model.{MediaType, Uri} diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolBilibili.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolBilibili.scala index 83e5dfa..3629732 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolBilibili.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolBilibili.scala @@ -1,10 +1,11 @@ package cc.sukazyo.cono.morny.social_share.query -import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{InlineQueryUnit, ITelegramQuery} import cc.sukazyo.cono.morny.reporter.MornyReport import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode} @@ -20,7 +21,7 @@ class ShareToolBilibili (using coeur: MornyCoeur) extends ITelegramQuery { private val LINK_PREFIX = "https://bilibili.com/video/" private val SHARE_FORMAT_HTML = "%s" - override def query (event: Update): List[InlineQueryUnit[_]] | Null = { + override def query (event: Update): List[InlineQueryUnit[?]] | Null = { if (event.inlineQuery.query == null) return null if (event.inlineQuery.query isBlank) return null @@ -36,7 +37,7 @@ class ShareToolBilibili (using coeur: MornyCoeur) extends ITelegramQuery { case _: IllegalArgumentException => return null; case e: IllegalStateException => - logger error exceptionLog(e) + logger `error` e.toLogString coeur.externalContext.consume[MornyReport](_.exception(e)) return null; diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolSocialContent.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolSocialContent.scala index 9b53bfe..cc1d71b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolSocialContent.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolSocialContent.scala @@ -9,14 +9,14 @@ import com.pengrad.telegrambot.model.Update class ShareToolSocialContent extends ITelegramQuery { - override def query (event: Update): List[InlineQueryUnit[_]] | Null = { + override def query (event: Update): List[InlineQueryUnit[?]] | Null = { val _queryRaw = event.inlineQuery.query val query = _queryRaw.trim match - case _startsWithTag if _startsWithTag startsWith "get " => + case _startsWithTag if _startsWithTag `startsWith` "get " => (_startsWithTag drop 4)trim - case _endsWithTag if _endsWithTag endsWith " get" => + case _endsWithTag if _endsWithTag `endsWith` " get" => (_endsWithTag dropRight 4)trim case _ => return null diff --git a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolTwitter.scala b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolTwitter.scala index 618cd85..673838c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolTwitter.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/social_share/query/ShareToolTwitter.scala @@ -8,7 +8,6 @@ import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.InlineQueryResultArticle import scala.language.postfixOps -import scala.util.matching.Regex class ShareToolTwitter extends ITelegramQuery { @@ -17,7 +16,7 @@ class ShareToolTwitter extends ITelegramQuery { private val TITLE_FX = "[tweet] Share as Fix-Tweet" private val ID_PREFIX_FX = "[morny/share/twitter/fxtwi]" - override def query (event: Update): List[InlineQueryUnit[_]] | Null = { + override def query (event: Update): List[InlineQueryUnit[?]] | Null = { if (event.inlineQuery.query == null) return null 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 4372d59..f2c0c50 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 @@ -4,7 +4,10 @@ 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.data.TelegramImages +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, MediaType} import org.http4s.dsl.io.* import org.http4s.headers.`Content-Type` @@ -17,10 +20,11 @@ class StickerService (using coeur: MornyCoeur) extends HttpService4Api { case GET -> Root / "sticker" / "id" / id => try { - val response = coeur.account execute GetFile(id) + val response = GetFile(id).execute(using coeur.account) if response.isOk then try { - val file = coeur.account getFileContent response.file + given TelegramBot = coeur.account + val file = response.file.getContent Ok(file) } catch { case e: IOException => diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/InlineRawText.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/InlineRawText.scala index 1143400..947da4f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/InlineRawText.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/InlineRawText.scala @@ -12,7 +12,7 @@ class InlineRawText extends ITelegramQuery { private val ID_PREFIX = "[morny/r/text]" private val TITLE = "Raw Text" - override def query (event: Update): List[InlineQueryUnit[_]] | Null = { + override def query (event: Update): List[InlineQueryUnit[?]] | Null = { if (event.inlineQuery.query == null || (event.inlineQuery.query isBlank)) return null diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/CommandEventHack.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/CommandEventHack.scala index 5808c0d..1942eaa 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/CommandEventHack.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/CommandEventHack.scala @@ -4,13 +4,15 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendSticker +import com.pengrad.telegrambot.TelegramBot import scala.language.postfixOps class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account override val name: String = "event_hack" override val aliases: List[ICommandAlias] = Nil @@ -24,15 +26,17 @@ class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) exte val x_mode = if (command.args nonEmpty) command.args(0) else "" def done_ok = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_WAITING ).replyToMessageId(event.message.messageId) + .unsafeExecute def done_forbiddenForAny = - coeur.account exec SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) + .unsafeExecute def doRegister (t: HackType): Unit = registerHack( @@ -43,7 +47,7 @@ class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) exte ) x_mode match case "any" => - if (coeur.trusted isTrusted event.message.from.id) + if (coeur.trusted isTrust event.message.from) doRegister(HackType ANY) done_ok else done_forbiddenForAny diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/EventHacker.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/EventHacker.scala index ed90caa..21ab6d4 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/EventHacker.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/event_hack/EventHacker.scala @@ -2,15 +2,17 @@ package cc.sukazyo.cono.morny.tele_utils.event_hack import cc.sukazyo.cono.morny.core.Log.logger import cc.sukazyo.cono.morny.core.MornyCoeur -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.google.gson.GsonBuilder import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot import scala.collection.mutable class EventHacker (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account private case class Hacker (from_chat: Long, from_message: Long): override def toString: String = s"$from_chat/$from_message" @@ -28,22 +30,23 @@ class EventHacker (using coeur: MornyCoeur) { case HackType.GROUP => s"{{$from_chat}}" case HackType.ANY => "[[]]" hackers += (record -> Hacker(from_chat, from_message)) - logger debug s"add hacker track $record" + logger `debug` s"add hacker track $record" def trigger (chat: Long, fromUser: Long)(using update: Update): Boolean = { - logger debug s"got event signed {{$chat}}(($fromUser))" + logger `debug` s"got event signed {{$chat}}(($fromUser))" val x: Hacker = if hackers contains s"(($fromUser))" then (hackers remove s"(($fromUser))") get else if hackers contains s"{{$chat}}" then (hackers remove s"{{$chat}}") get else if hackers contains "[[]]" then (hackers remove "[[]]") get else return false - logger debug s"hacked event by $x" + logger `debug` s"hacked event by $x" import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - coeur.account exec SendMessage( + SendMessage( x.from_chat, // language=html s"
${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}
" ).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt) + .unsafeExecute true } diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/CommandGetUser.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/CommandGetUser.scala index 4a5a018..9247c96 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/CommandGetUser.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/CommandGetUser.scala @@ -4,14 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand} import cc.sukazyo.cono.morny.util.tgapi.{InputCommand, Standardize} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.{execute, unsafeExecute} import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{GetChatMember, SendMessage} - -import scala.language.postfixOps +import com.pengrad.telegrambot.TelegramBot class CommandGetUser (using coeur: MornyCoeur) extends ITelegramCommand { + private given TelegramBot = coeur.account override val name: String = "user" override val aliases: List[ICommandAlias] = Nil @@ -23,46 +23,51 @@ class CommandGetUser (using coeur: MornyCoeur) extends ITelegramCommand { val args = command.args if (args.length > 1) - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, "[Unavailable] Too much arguments." ).replyToMessageId(event.message.messageId) + .unsafeExecute return val userId: Long = if (args nonEmpty) { try args(0) toLong catch case e: NumberFormatException => - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, s"[Unavailable] ${e.getMessage}" ).replyToMessageId(event.message.messageId) + .unsafeExecute return } else if (event.message.replyToMessage eq null) event.message.from.id else event.message.replyToMessage.from.id - val response = coeur.account execute GetChatMember(event.message.chat.id, userId) + val response = GetChatMember(event.message.chat.id, userId).execute if (response.chatMember eq null) - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, "[Unavailable] user not found." ).replyToMessageId(event.message.messageId) + .unsafeExecute return val user = response.chatMember.user if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID) - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, "$__channel_identify" ).replyToMessageId(event.message.messageId) + .unsafeExecute return; - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, - TelegramUserInformation getFormattedInformation user + TelegramUserInformation.getFormattedInformation(user) ).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/InlineMyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/InlineMyInformation.scala index 1173f75..5d50a82 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/InlineMyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/tele_utils/user_info/InlineMyInformation.scala @@ -13,7 +13,7 @@ class InlineMyInformation extends ITelegramQuery { private val ID_PREFIX = "[morny/info/me]" private val TITLE = "My Account Information" - override def query (event: Update): List[InlineQueryUnit[_]] | Null = { + override def query (event: Update): List[InlineQueryUnit[?]] | Null = { if !((event.inlineQuery.query eq null) || (event.inlineQuery.query isEmpty)) then return null @@ -21,7 +21,7 @@ class InlineMyInformation extends ITelegramQuery { InlineQueryUnit(InlineQueryResultArticle( inlineQueryId(ID_PREFIX), TITLE, new InputTextMessageContent( - TelegramUserInformation getFormattedInformation event.inlineQuery.from + TelegramUserInformation.getFormattedInformation(event.inlineQuery.from) ).parseMode(ParseMode HTML) )).isPersonal(true).cacheTime(10) ) diff --git a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/BotEventUniMeowTrigger.scala b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/BotEventUniMeowTrigger.scala index 201cd05..2ee16f5 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/BotEventUniMeowTrigger.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/BotEventUniMeowTrigger.scala @@ -10,17 +10,17 @@ class BotEventUniMeowTrigger (using commands: UniMeowCommandManager) extends Eve import event.* givenCxt >> { (input: InputCommand) => - logger trace s"got input command {$input} from event-context" + logger `trace` s"got input command {$input} from event-context" - for ((name, command_instance) <- commands.commands) { - logger trace s"checking uni-meow $name" + for ((name, command_instance) <- commands.getCommands) { + logger `trace` s"checking uni-meow $name" if (name == input.command) - logger trace "checked" + logger `trace` "checked" command_instance.execute(using input, event.update) event.setEventOk } - } || { logger trace "not command (for uni-meow)" } + } || { logger `trace` "not command (for uni-meow)" } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/UniMeowCommandManager.scala b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/UniMeowCommandManager.scala index fb31e24..a055cf0 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/UniMeowCommandManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/UniMeowCommandManager.scala @@ -1,17 +1,9 @@ package cc.sukazyo.cono.morny.uni_meow -import cc.sukazyo.cono.morny.core.bot.api.ISimpleCommand -import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap +import cc.sukazyo.cono.morny.core.bot.api.SimpleCommandManager -import scala.collection.mutable - -class UniMeowCommandManager { +class UniMeowCommandManager extends SimpleCommandManager{ - private[uni_meow] val commands: CommandMap = mutable.SeqMap.empty - def register [T <: ISimpleCommand] (commands: T*): Unit = - for (i <- commands) - this.commands += (i.name -> i) - for (alias <- i.aliases) - this.commands += (alias.name -> i) + protected[uni_meow] def getCommands: this.commands.type = this.commands } diff --git a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/创.scala b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/创.scala index 150195b..df2f8aa 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/创.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/创.scala @@ -3,12 +3,14 @@ package cc.sukazyo.cono.morny.uni_meow import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.* +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{MessageEntity, Update} import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot //noinspection NonAsciiCharacters class 创 (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account object Chuang extends ISimpleCommand { @@ -27,12 +29,13 @@ class 创 (using coeur: MornyCoeur) { return; val chuangText = 创.chuangText(text) - coeur.account exec SendMessage( + SendMessage( event.message.chat.id, chuangText ).entities( MessageEntity(MessageEntity.Type.pre, 0, chuangText.length) ).replyToMessageId(event.message.messageId) + .unsafeExecute } diff --git a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/喵呜.scala b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/喵呜.scala index 605ae51..0c84ae2 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/喵呜.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/喵呜.scala @@ -4,10 +4,11 @@ import cc.sukazyo.cono.morny.core.MornyCoeur import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand, ITelegramCommand} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.{Message, Update} import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendSticker} +import com.pengrad.telegrambot.TelegramBot import javax.swing.text.html.HTML import scala.annotation.unused @@ -15,6 +16,7 @@ import scala.language.postfixOps //noinspection NonAsciiCharacters class 喵呜 (using coeur: MornyCoeur) { + private given TelegramBot = coeur.account object 抱抱 extends ISimpleCommand { override val name: String = "抱抱" @@ -50,20 +52,22 @@ class 喵呜 (using coeur: MornyCoeur) { override val paramRule: String = "" override val description: String = "抽取一个神秘盒子" override def execute (using command: InputCommand, event: Update): Unit = { - coeur.account exec new SendSticker( + SendSticker( event.message.chat.id, TelegramStickers ID_PROGYNOVA ).replyToMessageId(event.message.messageId) + .unsafeExecute } } private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = { val isNew = event.message.replyToMessage == null val target = if (isNew) event.message else event.message.replyToMessage - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, if (isNew) whileNew else whileRec ).replyToMessageId(target.messageId).parseMode(ParseMode HTML) + .unsafeExecute } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/私わね.scala b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/私わね.scala index ca094b5..43b25cb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/uni_meow/私わね.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/uni_meow/私わね.scala @@ -5,12 +5,14 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.UseMath.over import cc.sukazyo.cono.morny.util.UseRandom.* -import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendMessage +import com.pengrad.telegrambot.TelegramBot //noinspection NonAsciiCharacters class 私わね (using coeur: MornyCoeur) extends ISimpleCommand { + private given TelegramBot = coeur.account override val name: String = "me" override val aliases: List[ICommandAlias] = Nil @@ -19,10 +21,11 @@ class 私わね (using coeur: MornyCoeur) extends ISimpleCommand { if ((1 over 521) chance_is true) { val text = "/打假" - coeur.account exec new SendMessage( + SendMessage( event.message.chat.id, text ).replyToMessageId(event.message.messageId) + .unsafeExecute } } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/CommonEncrypt.scala b/src/main/scala/cc/sukazyo/cono/morny/util/CommonEncrypt.scala index dfc4752..22de187 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/CommonEncrypt.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/CommonEncrypt.scala @@ -37,7 +37,7 @@ object CommonEncrypt { private def hash (data: Bin)(using algorithm: String): Bin = try { - MessageDigest.getInstance(algorithm) digest data + MessageDigest.getInstance(algorithm) `digest` data } catch case n: NoSuchAlgorithmException => throw IllegalStateException(n) @@ -47,7 +47,7 @@ object CommonEncrypt { * * $WhenString2Bin */ - def MD5 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "md5") + def MD5 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "md5") /** the [[https://en.wikipedia.org/wiki/SHA-1 SHA-1]] hash value of input [[Bin]] `data`. */ def SHA1 (data: Bin): Bin = hash(data)(using "sha1") @@ -55,7 +55,7 @@ object CommonEncrypt { * * $WhenString2Bin */ - def SHA1 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha1") + def SHA1 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha1") /** the [[https://en.wikipedia.org/wiki/SHA-2 SHA-2/256]] hash value of input [[Bin]] `data`. */ def SHA256 (data: Bin): Bin = hash(data)(using "sha256") @@ -63,7 +63,7 @@ object CommonEncrypt { * * $WhenString2Bin */ - def SHA256 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha256") + def SHA256 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha256") /** the [[https://en.wikipedia.org/wiki/SHA-2 SHA-2/512]] hash value of input [[Bin]] `data`. */ def SHA512 (data: Bin): Bin = hash(data)(using "sha512") @@ -71,7 +71,7 @@ object CommonEncrypt { * * $WhenString2Bin */ - def SHA512 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha512") + def SHA512 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha512") /** Try get the filename before it got encrypted. * @@ -89,10 +89,10 @@ object CommonEncrypt { * @return the file fullname removed the base64 file extension. */ def lint_base64FileName (encrypted: String): String = encrypted match - case i if i endsWith ".b64" => i dropRight ".b64".length - case ix if ix endsWith ".b64.txt" => ix dropRight ".b64.txt".length - case l if l endsWith ".base64" => l dropRight ".base64".length - case lx if lx endsWith ".base64.txt" => lx dropRight ".base64.txt".length + case i if i `endsWith` ".b64" => i dropRight ".b64".length + case ix if ix `endsWith` ".b64.txt" => ix dropRight ".b64.txt".length + case l if l `endsWith` ".base64" => l dropRight ".base64".length + case lx if lx `endsWith` ".base64.txt" => lx dropRight ".base64.txt".length case u => u /** Hash a [[Long]] id to [[Bin]] using [[MD5]] algorithm. diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala b/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala index 5f3d7a2..492edb3 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/EpochDateTime.scala @@ -8,10 +8,14 @@ object EpochDateTime { /** The UNIX Epoch Time in milliseconds. * * aka. Milliseconds since 00:00:00 UTC on Thursday, 1 January 1970. + * + * @since 1.1.1 */ type EpochMillis = Long object EpochMillis: /** convert a localtime with timezone to epoch milliseconds + * + * @since 1.1.1 * * @param time the local time in that timezone, should be formatted * in [[DateTimeFormatter.ISO_DATE_TIME]] @@ -22,7 +26,7 @@ object EpochDateTime { def apply (time: String, zone: String): EpochMillis = { val formatter = DateTimeFormatter.ISO_DATE_TIME val innerTime = LocalDateTime.parse(time, formatter) - val instant = innerTime.toInstant(ZoneOffset of zone) + val instant = innerTime.toInstant(ZoneOffset `of` zone) instant.toEpochMilli } def apply (time_zone: (String, String)): EpochMillis = @@ -33,8 +37,10 @@ object EpochDateTime { * * Due to the missing accuracy, the converted EpochMillis will * be always in 0ms aligned. + * + * @since 2.0.0 */ - def fromEpochSeconds (epochSeconds: EpochSeconds): EpochMillis = + infix def fromSeconds (epochSeconds: EpochSeconds): EpochMillis = epochSeconds.longValue * 1000L /** The UNIX Epoch Time in seconds. @@ -45,6 +51,8 @@ object EpochDateTime { * * Notice that, currently, it stores using [[Int]] (also the implementation * method of Telegram), which will only store times before 2038-01-19 03:14:07. + * + * @since 1.3.0 */ type EpochSeconds = Int @@ -56,13 +64,20 @@ object EpochDateTime { * * Notice that, currently, it stores using [[Short]] (also the implementation * method of Telegram), which will only store times before 2059-09-18. + * + * @since 1.3.0 */ type EpochDays = Short object EpochDays: - def fromEpochMillis (epochMillis: EpochMillis): EpochDays = + /** Convert a [[EpochMillis]] to [[EpochDays]]. Will be loss of precision. + * @since 2.0.0 + */ + infix def fromMillis (epochMillis: EpochMillis): EpochDays = (epochMillis / (1000*60*60*24)).toShort - /** Time duration/interval in milliseconds. */ + /** Time duration/interval in milliseconds. + * @since 1.3.0 + */ type DurationMillis = Long } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/FileUtils.scala b/src/main/scala/cc/sukazyo/cono/morny/util/FileUtils.scala index f1e146b..6a8bc7f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/FileUtils.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/FileUtils.scala @@ -18,7 +18,7 @@ object FileUtils { Using (FileInputStream(path)) { stream => len = stream.read(buffer) while (len != -1) - algo update (buffer, 0, len) + algo.update(buffer, 0, len) len = stream.read(buffer) } import ConvertByteHex.toHex diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/GivenContext.scala b/src/main/scala/cc/sukazyo/cono/morny/util/GivenContext.scala index 7814e48..0d3fe54 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/GivenContext.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/GivenContext.scala @@ -1,26 +1,17 @@ package cc.sukazyo.cono.morny.util import cc.sukazyo.cono.morny.util.GivenContext.{ContextNotGivenException, FolderClass, RequestItemClass} -import cc.sukazyo.messiva.utils.StackUtils import scala.annotation.targetName import scala.collection.mutable import scala.reflect.{classTag, ClassTag} -import scala.util.boundary object GivenContext { case class FolderClass (clazz: Option[Class[?]]) object FolderClass: def default: FolderClass = FolderClass(None) case class RequestItemClass (clazz: Class[?]) - private def lastNonGCStack: StackTraceElement = - boundary { - for (stack <- StackUtils.getStackTrace(0)) { - if (!stack.getClassName.startsWith(classOf[GivenContext].getName)) - boundary break stack - } - StackTraceElement("unknown", "unknown", "unknown", -1) - } + class ContextNotGivenException (using val requestItemClass: RequestItemClass, val folderClass: FolderClass = FolderClass.default, @@ -69,7 +60,12 @@ object GivenContext { * } catch case e: ContextNotGivenException => // if any of the above val is not available, it will catch the exception * e.printStackTrace() * }}} + * + * TODO: Tests + * + * @since 2.0.0 */ +//noinspection NoTargetNameAnnotationForOperatorLikeDefinition class GivenContext { private type ImplicitsMap [T <: Any] = mutable.HashMap[Class[?], T] @@ -77,7 +73,7 @@ class GivenContext { private val variables: ImplicitsMap[Any] = mutable.HashMap.empty private val variablesWithOwner: ImplicitsMap[ImplicitsMap[Any]] = mutable.HashMap.empty - def provide [T: ClassTag] (i: T): Unit = + infix def provide [T: ClassTag] (i: T): Unit = variables += (classTag[T].runtimeClass -> i) def << [T: ClassTag] (is: (Class[T], T)): Unit = val (_, i) = is @@ -91,7 +87,7 @@ class GivenContext { variables get t.clazz match case Some(i) => Right(i.asInstanceOf[T]) case None => Left(ContextNotGivenException()) - def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = + infix def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = this.use[T] match case Left(_) => ConsumeFailed[U]() case Right(i) => ConsumeSucceed[U](consumer(i)) @@ -101,7 +97,7 @@ class GivenContext { this.use[T].toTry.get def >>[T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = this.use[T,U](consumer) - def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] = + infix def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] = this.use[T,Any](consume) @targetName("ownedBy") @@ -112,7 +108,7 @@ class GivenContext { class OwnedContext [O: ClassTag] { - def provide [T: ClassTag] (i: T): Unit = + infix def provide [T: ClassTag] (i: T): Unit = (variablesWithOwner getOrElseUpdate (classTag[O].runtimeClass, mutable.HashMap.empty)) .addOne(classTag[T].runtimeClass -> i) def << [T: ClassTag] (is: (Class[T], T)): Unit = @@ -129,7 +125,7 @@ class GivenContext { case Some(i) => Right(i.asInstanceOf[T]) case None => Left(ContextNotGivenException()) case None => Left(ContextNotGivenException()) - def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = + infix def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = use[T] match case Left(_) => ConsumeFailed[U]() case Right(i) => ConsumeSucceed[U](consumer(i)) @@ -139,7 +135,7 @@ class GivenContext { this.use[T].toTry.get def >> [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = this.use[T,U](consumer) - def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] = + infix def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] = this.use[T,Any](consume) } 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 3fd795e..a0bfb76 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/StringEnsure.scala @@ -5,13 +5,68 @@ object StringEnsure { extension (str: String) { - def ensureSize(size: Int, paddingStr: Char = ' '): String = { + /** 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 + * with the given padding character to the right, until the length of the string is not smaller + * than the given length. + * + * For now, it does nothing if the length of the string is not smaller than the given length. + * + * @since 1.1.0 + * + * @param size The minimum length that the string should have. + * @param paddingStr The character that will be used to pad the string. Defaults to `' '` (a space). + * @return A string that have been ensured to have a length not smaller than the given length. + */ + infix def ensureSize(size: Int, paddingStr: Char = ' '): String = { if (str.length < size) { val padding = paddingStr.toString * (size-str.length) str + padding } else str } + /** Replace the String with a given char, keeps the String length and characters only at the start + * and end. + * + * This method can be used to de-sensitive some sensitive information, such as phone number, email, + * etc. The default setting is to keep the first 2 characters and the last 4 characters, and replace + * the rest with '*'. + * + * Notice that this method have un-defined behavior when the length of the String is less than + * the character that will be kept, so change the character length that will be kept in your need. + * + * Examples: + * {{{ + * scala> val someUserToken = "TOKEN_UV:V927c092FV$REFV[p':V someUserToken.deSensitive() + * val res1: String = TO**************************************eR)c + * + * scala> someUserToken.deSensitive(8, 4) + * val res2: String = TOKEN_UV********************************eR)c + * + * scala> someUserToken.deSensitive(10, 4, '?') + * val res3: String = TOKEN_UV:V??????????????????????????????eR)c + * + * scala> someUserToken.deSensitive(10, 4, '-') + * val res4: String = TOKEN_UV:V------------------------------eR)c + * + * scala> "short".deSensitive(5, 5, '-') + * val res5: String = shortshort + * }}} + * + * @since 1.2.0 + * + * @param keepStart The characters length that need to be kept at the start of the String. Defaults + * to `2`. + * @param keepEnd The characters length that need to be kept at the end of the String. Defaults to + * `4`. + * @param sensitive_cover The character that will be used to cover the sensitive information. + * Defaults to `*`. + * @return A string that have been de-sensitive. + */ def deSensitive (keepStart: Int = 2, keepEnd: Int = 4, sensitive_cover: Char = '*'): String = (str take keepStart) + (sensitive_cover.toString*(str.length-keepStart-keepEnd)) + (str takeRight keepEnd) diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/SttpPublic.scala b/src/main/scala/cc/sukazyo/cono/morny/util/SttpPublic.scala index 663388e..176d6b8 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/SttpPublic.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/SttpPublic.scala @@ -1,7 +1,8 @@ package cc.sukazyo.cono.morny.util import cc.sukazyo.cono.morny.core.MornySystem -import sttp.client3.basicRequest +import sttp.client3.{basicRequest, RequestT} +import sttp.client3 import sttp.model.Header object SttpPublic { @@ -17,13 +18,27 @@ object SttpPublic { private val key = "User-Agent" + /** The default Morny User-Agent header. + * + * It follows the following template: + * `MornyCoeur / ` + * + * @since 2.0.0 + */ val MORNY_CURRENT: Header = Header(key, s"MornyCoeur / ${MornySystem.VERSION}") } } - val mornyBasicRequest = + /** The basic request template for Morny's HTTP requests. + * + * It is a expansion of [[sttp.client3.basicRequest]] with the following features: + * - A [[Headers.UserAgent.MORNY_CURRENT]] User-Agent header. + * + * @since 2.0.0 + */ + val mornyBasicRequest: RequestT[client3.Empty, Either[String, String], Any] = basicRequest .header(Headers.UserAgent.MORNY_CURRENT, true) diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/UseMath.scala b/src/main/scala/cc/sukazyo/cono/morny/util/UseMath.scala index fdbfd1b..ef85621 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/UseMath.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/UseMath.scala @@ -7,7 +7,7 @@ object UseMath { extension (self: Int) { - def over (other: Int): Double = self.toDouble / other + infix def over (other: Int): Double = self.toDouble / other } @@ -17,7 +17,7 @@ object UseMath { } extension (base: Int) { - def percentageOf (another: Int): Int = + infix def percentageOf (another: Int): Int = Math.round((another.toDouble/base)*100).toInt } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/UseRandom.scala b/src/main/scala/cc/sukazyo/cono/morny/util/UseRandom.scala index b6eb096..1748059 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/UseRandom.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/UseRandom.scala @@ -10,7 +10,7 @@ import scala.util.Random object UseRandom { class ChancePossibility[T <: Any] (val one: T) (using possibility: Double) { - def nor[U] (another: U): T|U = + infix def nor[U] (another: U): T|U = if Random.nextDouble < possibility then one else another } @@ -19,7 +19,7 @@ object UseRandom { extension (num: Double) { - def chance_is[T <: Any] (one: T): ChancePossibility[T] = + infix def chance_is[T <: Any] (one: T): ChancePossibility[T] = ChancePossibility(one)(using num) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/UseStacks.scala b/src/main/scala/cc/sukazyo/cono/morny/util/UseStacks.scala index f7dffa9..5c68291 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/UseStacks.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/UseStacks.scala @@ -4,6 +4,7 @@ import cc.sukazyo.messiva.utils.StackUtils import scala.reflect.{classTag, ClassTag} import scala.util.boundary +import scala.util.boundary.break object UseStacks { @@ -11,7 +12,7 @@ object UseStacks { boundary { for (stack <- StackUtils.getStackTrace(1)) { if (!stack.getClassName.startsWith(classTag[T].runtimeClass.getName)) - boundary break stack + break(stack) } StackTraceElement("unknown", "unknown", "unknown", -1) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala b/src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala new file mode 100644 index 0000000..b93df57 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala @@ -0,0 +1,16 @@ +package cc.sukazyo.cono.morny.util + +import java.io.{PrintWriter, StringWriter} + +object UseThrowable { + + extension (t: Throwable) { + + def toLogString: String = + val stackTrace = StringWriter() + t `printStackTrace` PrintWriter(stackTrace) + stackTrace toString + + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala index 87d4aac..a51e832 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/schedule/Scheduler.scala @@ -93,14 +93,14 @@ class Scheduler { case readyToRun: Task => runtimeStatus = State.RUNNING - this setName readyToRun.name + this `setName` readyToRun.name try { readyToRun.main } catch case _: (Exception | Error) => {} runtimeStatus = State.RUNNING_POST - this setName s"${readyToRun.name}#post" + this `setName` s"${readyToRun.name}#post" // this if is used for check if post effect need to be // run. It is useless since the wait/notify changes. @@ -117,7 +117,7 @@ class Scheduler { } // currentRunning = null - this setName runnerName + this `setName` runnerName case needToWaitMillis: EpochMillis => runtimeStatus = State.WAITING @@ -133,7 +133,7 @@ class Scheduler { } } - runtime setName runnerName + runtime `setName` runnerName runtime.start() /** Name of the scheduler runner. @@ -179,7 +179,7 @@ class Scheduler { * succeed removed from task queue. */ def cancel (task: Task): Boolean = - taskList synchronized: + taskList.synchronized: try taskList remove task finally taskList.notifyAll() @@ -208,7 +208,7 @@ class Scheduler { * to make the scheduler avoid fails when machine fall asleep or some else conditions. */ def notifyIt(): Unit = - taskList synchronized: + taskList.synchronized: taskList.notifyAll() /** Stop the scheduler's runner, no matter how much task is not run yet. diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/InputCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/InputCommand.scala index e9f25c7..ab5e3fa 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/InputCommand.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/InputCommand.scala @@ -16,7 +16,7 @@ class InputCommand private ( object InputCommand { def apply (input: Array[String]): InputCommand = { - val _ex = if input.nonEmpty then input(0) split ("@", 2) else Array.empty[String] + val _ex = if input.nonEmpty then input(0).split("@", 2) else Array.empty[String] val _args = input drop 1 new InputCommand( if _ex.length > 1 then _ex(1) else null, diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala index 1449c3e..55e3d38 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/TelegramExtensions.scala @@ -1,12 +1,13 @@ package cc.sukazyo.cono.morny.util.tgapi import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException -import cc.sukazyo.cono.morny.util.EpochDateTime.{EpochMillis, EpochSeconds} +import cc.sukazyo.cono.morny.util.EpochDateTime.EpochSeconds import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.model.* import com.pengrad.telegrambot.request.{BaseRequest, GetChatMember} import com.pengrad.telegrambot.response.BaseResponse +import java.io.IOException import scala.annotation.targetName object TelegramExtensions { @@ -39,7 +40,7 @@ object TelegramExtensions { @throws[EventRuntimeException] def exec [T <: BaseRequest[T, R], R <: BaseResponse] (request: BaseRequest[T, R], onError_message: String = ""): R = { try { - val response = bot execute request + val response = bot `execute` request if response isOk then return response throw EventRuntimeException.ActionFailed( if onError_message isEmpty then response.errorCode toString else onError_message, @@ -229,11 +230,14 @@ object TelegramExtensions { * It needs to execute a request getting chat member, so it requires a live [[TelegramBot]] instance, * and the bot needs to be a member of this chat so that it can read the chat member. * + * This method will only use the [[Chat]]'s [[Chat.id]], so it is safe to call on a [[LimboChat]]. + * * **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow. * * @see [[memberHasPermission]] * - * @param user The user that want to check if they are a member of this chat. + * @param user The user that want to check if they are a member of this chat.Only its [[User.id]] will + * be used so it is safe using a [[LimboUser]]. * @return [[true]] when the user is a member of this chat, [[false]] otherwise. * * @since 1.0.0 @@ -257,9 +261,14 @@ object TelegramExtensions { * | [[ChatMember.Status.left]] | -3 | * | [[ChatMember.Status.kicked]] | -5 | * + * This method will only use the [[Chat]]'s [[Chat.id]], so it is safe to call on a [[LimboChat]]. + * * **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow. - * - * @param user The user that wanted to check if they have the permission. + * + * @since 1.0.0 + * + * @param user The user that wanted to check if they have the permission. Only its [[User.id]] will + * be used so it is safe using a [[LimboUser]]. * @param permission The required permission level. * @param bot A live [[TelegramBot]] that will be used to execute the getChatMember request. It * should be a member of this chat so that can read the chat member for this method works. @@ -288,8 +297,8 @@ object TelegramExtensions { case ChatMember.Status.kicked => KICKED def apply (chatMember: ChatMember): UserPermissionLevel = apply(chatMember.status) - import Bot.* - val chatMember: ChatMember = (bot exec GetChatMember(chat.id, user.id)).chatMember + import Requests.execute + val chatMember: ChatMember = GetChatMember(chat.id, user.id).execute(using bot).chatMember if chatMember eq null then false else UserPermissionLevel(chatMember) >= UserPermissionLevel(permission) @@ -309,7 +318,65 @@ object TelegramExtensions { }} + object File { extension (self: File) { + + /** Alias of [[TelegramBot.getFileContent]] */ + @throws[IOException] + def getContent (using bot: TelegramBot): Array[Byte] = + bot.getFileContent(self) + + /** Alias of [[TelegramBot.getFullFilePath]] */ + def getFullPath (using bot: TelegramBot): String = + bot.getFullFilePath(self) + + }} + + object Requests { extension [T<:BaseRequest[T,R], R<:BaseResponse] (self: BaseRequest[T,R]) { + /** Run this request with the given bot. Returns [[BaseResponse response]] if succeed, or throws + * [[EventRuntimeException]] if run failed. + * + * This method is an alias for [[Bot.exec]] + * + * @since 2.0.0 + */ + @throws[EventRuntimeException] + def unsafeExecute (using bot: TelegramBot): R = + import Bot.exec + bot.exec(self) + /** Run this request with the given bot and returns no matter it succeed or not. + * + * Notice that if there's some client errors (like network error, etc.), this method will still + * throws an Exception following the [[TelegramBot.execute]]'s behavior. + * + * This method is an alias for the native [[TelegramBot.execute]]. + * + * @since 2.0.0 + */ + def execute (using bot: TelegramBot): R = + bot.execute(self) + }} + + /** A [[User]] instance with only a [[User.id]] is defined. + * + * This is only for capabilities that some method need a [[User]] but only need its [[User.id]]. If + * you don't have a [[User]] instance for some reason, you can use this instead. + * + * Many methods may crashes on this class for this is highly mutilated. Use this only the method declares + * that using this is safe. + * + * @since 1.0.0 + */ class LimboUser (id: Long) extends User(id) + /** A [[Chat]] instance with only a [[Chat.id]] is defined. + * + * This is only for capabilities that some method need a [[Chat]] but only need its [[Chat.id]]. If + * you don't have a [[Chat]] instance for some reason, you can use this instead. + * + * Many methods may crashes on this class for this is highly mutilated. Use this only the method declares + * that using this is safe. + * + * @since 1.0.0 + */ class LimboChat (val _id: Long) extends Chat() { override val id: java.lang.Long = _id } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/formatting/TelegramParseEscape.scala b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/formatting/TelegramParseEscape.scala index 208646a..d8dccf7 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/formatting/TelegramParseEscape.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/tgapi/formatting/TelegramParseEscape.scala @@ -54,13 +54,13 @@ object TelegramParseEscape { case "br" => result += TextNode("\n") case "tg-emoji" => - if elem.attributes.hasKey("emoji-id") then + if elem.attributes `hasKey` "emoji-id" then result += produceChildNodes(elem) else result += TextNode(elem.text) case "img" => - if elem.attributes hasKey "alt" then - result += TextNode(s"[${elem attr "alt"}]") + if elem.attributes `hasKey` "alt" then + result += TextNode(s"[${elem.attr("alt")}]") case _ => for (i <- cleanupHtml(elem.childNodes.asScala.toSeq)) result += i } diff --git a/src/main/scala/cc/sukazyo/cono/morny/util/time/WatchDog.scala b/src/main/scala/cc/sukazyo/cono/morny/util/time/WatchDog.scala index ffa996e..23c57ec 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/util/time/WatchDog.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/util/time/WatchDog.scala @@ -9,8 +9,8 @@ trait WatchDog (val isDaemonIt: Boolean = true) extends Thread { val overloadMillis: DurationMillis = tickSpeedMillis + (tickSpeedMillis/2) private var previousTickTimeMillis: Option[EpochMillis] = None - this setName threadName - this setDaemon isDaemonIt + this `setName` threadName + this `setDaemon` isDaemonIt this.start() diff --git a/src/test/scala/cc/sukazyo/cono/morny/MornyCLI.scala b/src/test/scala/cc/sukazyo/cono/morny/MornyCLI.scala index 0f003d1..9282acc 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/MornyCLI.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/MornyCLI.scala @@ -8,6 +8,8 @@ import scala.io.StdIn @main def MornyCLI (): Unit = { print("$ java -jar morny-coeur-\"+MornySystem.VERSION_FULL+\".jar ") - ServerMain main UniversalCommand(StdIn readLine) + ServerMain.main( + UniversalCommand(StdIn readLine) + ) } diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/daemon/MedicationTimerTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/daemon/MedicationTimerTest.scala index 5dfcae7..d8e4ece 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/daemon/MedicationTimerTest.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/daemon/MedicationTimerTest.scala @@ -26,7 +26,7 @@ class MedicationTimerTest extends MornyTests with TableDrivenPropertyChecks { forAll(examples) { (current, notifyAt, useTimezone, nextNotifyTime) => val _curr = EpochMillis(current) - val _tz = ZoneOffset of useTimezone + val _tz = ZoneOffset `of` useTimezone val _next = EpochMillis(nextNotifyTime) s"at time [$_curr], and need to be notify at hours ${notifyAt.mkString(",")} with $_tz :" - { diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala index a766428..47e8ff0 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/utils/EpochDateTimeTest.scala @@ -19,7 +19,7 @@ class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks { forAll(examples) { (epochSeconds, epochMillis) => s"EpochSeconds($epochSeconds) should be converted to EpochMillis($epochMillis)" in { - (EpochMillis fromEpochSeconds epochSeconds) shouldEqual epochMillis + (EpochMillis fromSeconds epochSeconds) shouldEqual epochMillis } } @@ -42,7 +42,7 @@ class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks { forAll(examples) { (epochMillis, epochDays) => s"EpochMillis($epochMillis) should be converted to EpochDays($epochDays)" in { - (EpochDays fromEpochMillis epochMillis) shouldEqual epochDays + (EpochDays fromMillis epochMillis) shouldEqual epochDays } } diff --git a/src/test/scala/cc/sukazyo/cono/morny/test/utils/schedule/TaskBasicTest.scala b/src/test/scala/cc/sukazyo/cono/morny/test/utils/schedule/TaskBasicTest.scala index 0883040..ccf4e02 100644 --- a/src/test/scala/cc/sukazyo/cono/morny/test/utils/schedule/TaskBasicTest.scala +++ b/src/test/scala/cc/sukazyo/cono/morny/test/utils/schedule/TaskBasicTest.scala @@ -32,7 +32,7 @@ class TaskBasicTest extends MornyTests { "task can be sync executed by calling its main method." taggedAs Slow in { - Thread.currentThread setName "parent-thread" + Thread.currentThread `setName` "parent-thread" val data = StringBuilder("") val task = Task("some-task", 0L, { Thread.sleep(100)