diff --git a/build.gradle b/build.gradle index deb0e57..39c8d01 100644 --- a/build.gradle +++ b/build.gradle @@ -110,8 +110,6 @@ tasks.withType(ScalaCompile).configureEach { scalaCompileOptions.encoding = proj_file_encoding.name() scalaCompileOptions.additionalParameters.add "-language:postfixOps" -// scalaCompileOptions.additionalParameters.add("-Yexplicit-nulls") -// scalaCompileOptions.additionalParameters.add "-language:experimental.saferExceptions" } @@ -196,10 +194,15 @@ publishing { } } publications { + //noinspection GroovyAssignabilityCheck main (MavenPublication) { + //noinspection GroovyAssignabilityCheck from components.java + //noinspection GroovyAssignabilityCheck groupId = proj_group + //noinspection GroovyAssignabilityCheck artifactId = proj_archive_name + //noinspection GroovyAssignabilityCheck version = proj_version } } diff --git a/gradle.properties b/gradle.properties index 6f20a80..46a5b30 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s VERSION = 1.0.0-RC5 USE_DELTA = true -VERSION_DELTA = scala1 +VERSION_DELTA = scala2 CODENAME = beiping diff --git a/src/main/scala/cc/sukazyo/cono/morny/MornyAssets.scala b/src/main/scala/cc/sukazyo/cono/morny/MornyAssets.scala index 489ac36..c331ea9 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/MornyAssets.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/MornyAssets.scala @@ -4,6 +4,8 @@ import cc.sukazyo.restools.ResourcesPackage object MornyAssets { + class AssetsException (caused: Throwable) extends Exception("Cannot read assets file.", caused) + val pack: ResourcesPackage = ResourcesPackage(MornyAssets.getClass, "assets_morny") } diff --git a/src/main/scala/cc/sukazyo/cono/morny/MornyCoeur.scala b/src/main/scala/cc/sukazyo/cono/morny/MornyCoeur.scala index 1860236..4463678 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/MornyCoeur.scala @@ -5,7 +5,8 @@ import cc.sukazyo.cono.morny.daemon.MornyDaemons import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.MornyCoeur.THREAD_MORNY_EXIT import cc.sukazyo.cono.morny.bot.api.TelegramUpdatesListener -import cc.sukazyo.cono.morny.bot.event.MornyEventListeners +import cc.sukazyo.cono.morny.bot.event.{MornyEventListeners, MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock} +import cc.sukazyo.cono.morny.bot.query.MornyQueries import cc.sukazyo.cono.morny.util.tgapi.ExtraAction import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.request.GetMe @@ -17,53 +18,15 @@ object MornyCoeur { val THREAD_MORNY_EXIT = "morny-exiting" - private var INSTANCE: MornyCoeur|Null = _ - - val coeurStartTimestamp: Long = ServerMain.systemStartupTime - - def account: TelegramBot = INSTANCE.account - def username: String = INSTANCE.username - def userid: Long = INSTANCE.userid - def config: MornyConfig = INSTANCE.my_config - def trusted: MornyTrusted = INSTANCE.trusted - def extra: ExtraAction = INSTANCE.extra - - def available: Boolean = INSTANCE != null - def callSaveData(): Unit = INSTANCE.saveDataAll() - - def exitReason: AnyRef|Null = INSTANCE.whileExit_reason - - def exit (status: Int, reason: AnyRef): Unit = - INSTANCE.whileExit_reason = reason - System exit status - - def init (using config: MornyConfig): Unit = { - - if (INSTANCE ne null) - logger error "Coeur already started!!!" - return; - - logger info "Coeur starting..." - INSTANCE = MornyCoeur() - - MornyDaemons.start() - - logger info "start telegram event listening" - MornyEventListeners.registerAllEvents() - INSTANCE.account.setUpdatesListener(TelegramUpdatesListener) - - if config.commandLoginRefresh then - logger info "resetting telegram command list" - MornyCommands.automaticTGListUpdate() - - logger info "Coeur start complete." - - } - } -class MornyCoeur (using config: MornyConfig) { - def my_config: MornyConfig = config +class MornyCoeur (using val config: MornyConfig) { + + given MornyCoeur = this + + ///>>> BLOCK START instance configure & startup stage 1 + + logger info "Coeur starting..." logger info s"args key:\n ${config.telegramBotKey}" if config.telegramBotUsername ne null then @@ -76,13 +39,47 @@ class MornyCoeur (using config: MornyConfig) { configure_exitCleanup() + ///<<< BLOCK END instance configure & startup stage 1 + + /** [[TelegramBot]] account of this Morny */ val account: TelegramBot = __loginResult.account + /** [[account]]'s telegram username */ val username: String = __loginResult.username + /** [[account]]'s telegram user id */ val userid: Long = __loginResult.userid - val trusted: MornyTrusted = MornyTrusted(using this) + /** current Morny's [[MornyTrusted]] instance */ + val trusted: MornyTrusted = MornyTrusted() + /** current Morny's [[ExtraAction]] toolset */ val extra: ExtraAction = ExtraAction as __loginResult.account - var whileExit_reason: AnyRef|Null = _ + private val updatesListener: TelegramUpdatesListener = TelegramUpdatesListener() + + val daemons: MornyDaemons = MornyDaemons() + updatesListener.manager register MornyOnUpdateTimestampOffsetLock() + val commands: MornyCommands = MornyCommands() + //noinspection ScalaWeakerAccess + val queries: MornyQueries = MornyQueries() + updatesListener.manager register MornyOnTelegramCommand(using commands) + updatesListener.manager register MornyOnInlineQuery(using queries) + val events: MornyEventListeners = MornyEventListeners(using updatesListener.manager) + + /** inner value: about why morny exit, used in [[daemon.MornyReport]]. */ + private var whileExit_reason: AnyRef|Null = _ + def exitReason: AnyRef|Null = whileExit_reason + val coeurStartTimestamp: Long = ServerMain.systemStartupTime + + ///>>> BLOCK START instance configure & startup stage 2 + + daemons.start() + logger info "start telegram event listening" + account setUpdatesListener updatesListener + if config.commandLoginRefresh then + logger info "resetting telegram command list" + commands.automaticTGListUpdate() + + logger info "Coeur start complete." + + ///<<< BLOCK END instance configure & startup stage 2 def saveDataAll(): Unit = { // nothing to do @@ -90,15 +87,19 @@ class MornyCoeur (using config: MornyConfig) { } private def exitCleanup (): Unit = { - MornyDaemons.stop() + daemons.stop() if config.commandLogoutClear then - MornyCommands.automaticTGListRemove() + commands.automaticTGListRemove() } private def configure_exitCleanup (): Unit = { Runtime.getRuntime.addShutdownHook(new Thread(() => exitCleanup(), THREAD_MORNY_EXIT)) } + def exit (status: Int, reason: AnyRef): Unit = + whileExit_reason = reason + System exit status + private case class LoginResult(account: TelegramBot, username: String, userid: Long) private def login (): LoginResult|Null = { diff --git a/src/main/scala/cc/sukazyo/cono/morny/MornyConfig.java b/src/main/scala/cc/sukazyo/cono/morny/MornyConfig.java index 2702a61..692706d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/MornyConfig.java +++ b/src/main/scala/cc/sukazyo/cono/morny/MornyConfig.java @@ -86,7 +86,7 @@ public class MornyConfig { /** * morny 的事件忽略前缀时间
*
- * {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock} + * {@link cc.sukazyo.cono.morny.bot.event.MornyOnUpdateTimestampOffsetLock} * 会根据这里定义的时间戳取消掉比此时间更早的事件链 */ public final long eventOutdatedTimestamp; diff --git a/src/main/scala/cc/sukazyo/cono/morny/MornySystem.scala b/src/main/scala/cc/sukazyo/cono/morny/MornySystem.scala index b355595..97866db 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/MornySystem.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/MornySystem.scala @@ -2,7 +2,6 @@ package cc.sukazyo.cono.morny import cc.sukazyo.cono.morny.internal.BuildConfigField import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} -import cc.sukazyo.cono.morny.daemon.MornyReport import cc.sukazyo.cono.morny.util.FileUtils import java.io.IOException @@ -42,7 +41,7 @@ object MornySystem { "" case n: NoSuchAlgorithmException => logger error exceptionLog(n) - MornyReport.exception(n, "") +// MornyReport.exception(n, "") // todo: will not implemented "" } diff --git a/src/main/scala/cc/sukazyo/cono/morny/ServerMain.scala b/src/main/scala/cc/sukazyo/cono/morny/ServerMain.scala index b481eb0..5947797 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/ServerMain.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/ServerMain.scala @@ -143,7 +143,7 @@ object ServerMain { Thread.currentThread setName THREAD_MORNY_INIT try - MornyCoeur.init(using config build) + MornyCoeur(using config build) catch { case _: CheckFailure.NullTelegramBotKey => logger.info("Parameter required has no value:\n --token.") diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala index 67b7047..e4ab6de 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala @@ -1,8 +1,9 @@ package cc.sukazyo.cono.morny.bot.api +import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.model.Update -trait EventListener { +trait EventListener (using MornyCoeur) { def onMessage (using Update): Boolean = false def onEditedMessage (using Update): Boolean = false diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala index 98b7852..dc61951 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala @@ -1,8 +1,7 @@ package cc.sukazyo.cono.morny.bot.api -import cc.sukazyo.cono.morny.Log +import cc.sukazyo.cono.morny.{Log, MornyCoeur} import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} -import cc.sukazyo.cono.morny.daemon.MornyReport import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException import com.google.gson.GsonBuilder import com.pengrad.telegrambot.model.Update @@ -10,7 +9,7 @@ import com.pengrad.telegrambot.model.Update import scala.collection.mutable import scala.language.postfixOps -object EventListenerManager { +class EventListenerManager (using coeur: MornyCoeur) { private val listeners = mutable.Queue.empty[EventListener] @@ -69,7 +68,7 @@ object EventListenerManager { ) indent 4) ++= "\n" case _ => logger error errorMessage.toString - MornyReport.exception(e, "on event running") + coeur.daemons.reporter.exception(e, "on event running") } if (status isOk) return } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/TelegramUpdatesListener.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/TelegramUpdatesListener.scala index 83c2101..dd9f71e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/TelegramUpdatesListener.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/TelegramUpdatesListener.scala @@ -1,16 +1,19 @@ package cc.sukazyo.cono.morny.bot.api +import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.UpdatesListener import com.pengrad.telegrambot.model.Update import java.util import scala.jdk.CollectionConverters.* -object TelegramUpdatesListener extends UpdatesListener { +class TelegramUpdatesListener (using MornyCoeur) extends UpdatesListener { + + val manager = EventListenerManager() override def process (updates: util.List[Update]): Int = { for (update <- updates.asScala) - EventListenerManager.publishUpdate(using update) + manager.publishUpdate(using update) UpdatesListener.CONFIRMED_UPDATES_ALL } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.scala index fb01c86..56bec4f 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.scala @@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.{DeleteMessage, GetChatMember, SendSticke import scala.language.postfixOps -object DirectMsgClear extends ISimpleCommand { +class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand { override val name: String = "r" override val aliases: Array[ICommandAlias] | Null = null @@ -19,16 +19,16 @@ object DirectMsgClear extends ISimpleCommand { logger debug "executing command /r" if (event.message.replyToMessage == null) return; logger trace "message is a reply" - if (event.message.replyToMessage.from.id != MornyCoeur.userid) return; + 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 = MornyCoeur.trusted isTrusted event.message.from.id + val isTrusted = coeur.trusted isTrusted event.message.from.id // todo: - // it does not work. due to the Telegram Bot API doesn't provide - // nested replyToMessage, so currently the trusted check by - // replyToMessage.replyToMessage will not work! + // it does not work. due to the Telegram Bot API doesn't provide + // nested replyToMessage, so currently the trusted check by + // replyToMessage.replyToMessage will not work! def _isReplyTrusted: Boolean = if (event.message.replyToMessage.replyToMessage == null) false else if (event.message.replyToMessage.replyToMessage.from.id == event.message.from.id) true @@ -36,19 +36,19 @@ object DirectMsgClear extends ISimpleCommand { if (isTrusted || _isReplyTrusted) { - MornyCoeur.extra exec DeleteMessage( + coeur.extra exec DeleteMessage( event.message.chat.id, event.message.replyToMessage.messageId ) def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private def _isPermission: Boolean = - (MornyCoeur.extra exec GetChatMember(event.message.chat.id, event.message.from.id)) + (coeur.extra exec GetChatMember(event.message.chat.id, event.message.from.id)) .chatMember.canDeleteMessages if (_isPrivate || _isPermission) { - MornyCoeur.extra exec DeleteMessage(event.message.chat.id, event.message.messageId) + coeur.extra exec DeleteMessage(event.message.chat.id, event.message.messageId) } - } else MornyCoeur.extra exec SendSticker( + } else coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Encryptor.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Encryptor.scala index f1faef6..8bc5bba 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Encryptor.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Encryptor.scala @@ -2,7 +2,6 @@ package cc.sukazyo.cono.morny.bot.command import cc.sukazyo.cono.morny.Log.logger import cc.sukazyo.cono.morny.MornyCoeur -import cc.sukazyo.cono.morny.daemon.MornyReport import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.CommonEncrypt @@ -17,7 +16,7 @@ import java.util.Base64 import scala.language.postfixOps /** Provides Telegram Command __`/encrypt`__. */ -object Encryptor extends ITelegramCommand { +class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand { override val name: String = "encrypt" override val aliases: Array[ICommandAlias] | Null = null @@ -48,7 +47,7 @@ object Encryptor extends ITelegramCommand { val mod_uppercase = if (args.length > 1) { if (args.length < 3 && _is_mod_u(args(1))) true else - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) @@ -74,11 +73,11 @@ object Encryptor extends ITelegramCommand { val _r = event.message.replyToMessage if ((_r ne null) && (_r.document ne null)) { try {XFile( - MornyCoeur.account getFileContent (MornyCoeur.extra exec GetFile(_r.document.fileId)).file, + coeur.account getFileContent (coeur.extra exec GetFile(_r.document.fileId)).file, _r.document.fileName )} catch case e: IOException => logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" - MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI") + coeur.daemons.reporter.exception(e, "NetworkRequest error: TelegramFileAPI") return } else if ((_r ne null) && (_r.photo ne null)) { try { @@ -92,22 +91,23 @@ object Encryptor extends ITelegramCommand { if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.") import cc.sukazyo.cono.morny.util.UseRandom.rand_id XFile( - MornyCoeur.account getFileContent (MornyCoeur.extra exec GetFile(_photo_origin.fileId)).file, + coeur.account getFileContent (coeur.extra exec GetFile(_photo_origin.fileId)).file, s"photo$rand_id.png" ) } catch case e: IOException => + //noinspection DuplicatedCode logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" - MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI") + coeur.daemons.reporter.exception(e, "NetworkRequest error: TelegramFileAPI") return case e: IllegalArgumentException => logger warn s"FileProcess error: PhotoSize:\n\t${e.getMessage}" - MornyReport.exception(e, "FileProcess error: PhotoSize") + coeur.daemons.reporter.exception(e, "FileProcess error: PhotoSize") return } else if ((_r ne null) && (_r.text ne null)) { XText(_r.text) } else { - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, "null" ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) @@ -153,7 +153,7 @@ object Encryptor extends ITelegramCommand { _tool_b64d.decode, CommonEncrypt.lint_base64FileName ) } catch case _: IllegalArgumentException => - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_404 // todo: is here better erro notify? ).replyToMessageId(event.message.messageId) @@ -163,7 +163,7 @@ object Encryptor extends ITelegramCommand { case "sha256" => genResult_hash(input, SHA256) case "sha512" => genResult_hash(input, SHA512) case _ => - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) @@ -173,13 +173,13 @@ object Encryptor extends ITelegramCommand { // output result match case _file: EXFile => - MornyCoeur.extra exec SendDocument( + coeur.extra exec SendDocument( event.message.chat.id, _file.result ).fileName(_file.resultName).replyToMessageId(event.message.messageId) case _text: EXTextLike => import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, s"
${h(_text.text)}
" ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) @@ -211,7 +211,7 @@ object Encryptor extends ITelegramCommand { * */ private def echoHelp(chat: Long, replyTo: Int): Unit = - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( chat, s"""base64, b64 |base64url, base64u, b64u diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala index 963c80a..e6a4fdb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala @@ -1,7 +1,5 @@ package cc.sukazyo.cono.morny.bot.command import cc.sukazyo.cono.morny.MornyCoeur -import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle -import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle.{registerHack, HackType} import cc.sukazyo.cono.morny.data.TelegramStickers import cc.sukazyo.cono.morny.util.tgapi.InputCommand import com.pengrad.telegrambot.model.Update @@ -9,7 +7,7 @@ import com.pengrad.telegrambot.request.SendSticker import scala.language.postfixOps -object EventHack extends ITelegramCommand { +class EventHack (using coeur: MornyCoeur) extends ITelegramCommand { override val name: String = "event_hack" override val aliases: Array[ICommandAlias] | Null = null @@ -18,15 +16,17 @@ object EventHack extends ITelegramCommand { override def execute (using command: InputCommand, event: Update): Unit = { + import coeur.daemons.eventHack.{registerHack, HackType} + val x_mode = if (command.args nonEmpty) command.args(0) else "" def done_ok = - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_WAITING ).replyToMessageId(event.message.messageId) def done_forbiddenForAny = - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) @@ -40,7 +40,7 @@ object EventHack extends ITelegramCommand { ) x_mode match case "any" => - if (MornyCoeur.trusted isTrusted event.message.from.id) + if (coeur.trusted isTrusted event.message.from.id) doRegister(HackType ANY) done_ok else done_forbiddenForAny diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala index 78013bd..627d737 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala @@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.{GetChatMember, SendMessage} import scala.language.postfixOps -object GetUsernameAndId extends ITelegramCommand { +class GetUsernameAndId (using coeur: MornyCoeur) extends ITelegramCommand { override val name: String = "user" override val aliases: Array[ICommandAlias] | Null = null @@ -21,7 +21,7 @@ object GetUsernameAndId extends ITelegramCommand { val args = command.args if (args.length > 1) - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, "[Unavailable] Too much arguments." ).replyToMessageId(event.message.messageId) @@ -31,7 +31,7 @@ object GetUsernameAndId extends ITelegramCommand { if (args nonEmpty) { try args(0) toLong catch case e: NumberFormatException => - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, s"[Unavailable] ${e.getMessage}" ).replyToMessageId(event.message.messageId) @@ -39,10 +39,10 @@ object GetUsernameAndId extends ITelegramCommand { } else if (event.message.replyToMessage eq null) event.message.from.id else event.message.replyToMessage.from.id - val response = MornyCoeur.account execute GetChatMember(event.message.chat.id, userId) + val response = coeur.account execute GetChatMember(event.message.chat.id, userId) if (response.chatMember eq null) - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, "[Unavailable] user not found." ).replyToMessageId(event.message.messageId) @@ -51,13 +51,13 @@ object GetUsernameAndId extends ITelegramCommand { val user = response.chatMember.user if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID) - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, "$__channel_identify" ).replyToMessageId(event.message.messageId) return; - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, TelegramUserInformation getFormattedInformation user ).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala index a5d17b6..d6c80b0 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala @@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.SendMessage import scala.language.postfixOps -object IP186Query { +class IP186Query (using coeur: MornyCoeur) { private enum Subs (val cmd: String): case IP extends Subs("ip") @@ -34,7 +34,7 @@ object IP186Query { if (command.args isEmpty) if event.message.replyToMessage eq null then null else event.message.replyToMessage.text else if (command.args.length > 1) - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, "[Unavailable] Too much arguments." ).replyToMessageId(event.message.messageId) @@ -42,7 +42,7 @@ object IP186Query { else command.args(0) if (target eq null) - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, "[Unavailable] No ip defined." ).replyToMessageId(event.message.messageId) @@ -57,7 +57,7 @@ object IP186Query { case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target) case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}") - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, s"""${h(response.url)} |${h(response.body)}""" @@ -65,7 +65,7 @@ object IP186Query { ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) } catch case e: Exception => - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message().chat().id(), s"""[Exception] in query: |${h(e.getMessage)}""" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala index 4776d68..21fdf46 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyCommands.scala @@ -11,7 +11,7 @@ import scala.collection.{mutable, SeqMap} import scala.collection.mutable.ArrayBuffer import scala.language.postfixOps -object MornyCommands { +class MornyCommands (using coeur: MornyCoeur) { private type CommandMap = SeqMap[String, ISimpleCommand] private def CommandMap (commands: ISimpleCommand*): CommandMap = @@ -22,41 +22,47 @@ object MornyCommands { stash += (alias.name -> i) stash + private val $MornyHellos = MornyHellos() + private val $IP186Query = IP186Query() + private val $MornyInformation = MornyInformation() + private val $MornyInformationOlds = MornyInformationOlds(using $MornyInformation) + private val $MornyManagers = MornyManagers() + //noinspection NonAsciiCharacters + private val $喵呜 = 喵呜() private val commands: CommandMap = CommandMap( - MornyHellos.On, - MornyHellos.On, - MornyHellos.Hello, - MornyInfoOnStart, - GetUsernameAndId, - EventHack, - Nbnhhsh, - IP186Query.IP, - IP186Query.Whois, - Encryptor, - MornyManagers.SaveData, - MornyInformation, - MornyInformationOlds.Version, - MornyInformationOlds.Runtime, - MornyOldJrrp, - MornyManagers.Exit, + $MornyHellos.On, + $MornyHellos.Hello, + MornyInfoOnStart(), + GetUsernameAndId(), + EventHack(), + Nbnhhsh(), + $IP186Query.IP, + $IP186Query.Whois, + Encryptor(), + $MornyManagers.SaveData, + $MornyInformation, + $MornyInformationOlds.Version, + $MornyInformationOlds.Runtime, + MornyOldJrrp(), + $MornyManagers.Exit, - Testing, - DirectMsgClear, + Testing(), + DirectMsgClear(), //noinspection NonAsciiCharacters - 私わね, + 私わね(), //noinspection NonAsciiCharacters - 喵呜.Progynova + $喵呜.Progynova ) //noinspection NonAsciiCharacters val commands_uni: CommandMap = CommandMap( - 喵呜.抱抱, - 喵呜.揉揉, - 喵呜.贴贴, - 喵呜.蹭蹭 + $喵呜.抱抱, + $喵呜.揉揉, + $喵呜.贴贴, + $喵呜.蹭蹭 ) def execute (using command: InputCommand, event: Update): Boolean = { @@ -69,7 +75,7 @@ object MornyCommands { private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = { if command.target eq null then false else - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) @@ -79,14 +85,14 @@ object MornyCommands { def automaticTGListUpdate (): Unit = { val listing = commands_toTelegramList automaticTGListRemove() - MornyCoeur.extra exec SetMyCommands(listing:_*) + coeur.extra exec SetMyCommands(listing:_*) logger info s"""automatic updated telegram command list : |${commandsTelegramList_toString(listing)}""".stripMargin } def automaticTGListRemove (): Unit = { - MornyCoeur.extra exec DeleteMyCommands() + coeur.extra exec DeleteMyCommands() logger info "cleaned up command list" } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala index 80c4519..5c6abb6 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyHellos.scala @@ -8,7 +8,7 @@ import com.pengrad.telegrambot.request.SendSticker import scala.language.postfixOps -object MornyHellos { +class MornyHellos (using coeur: MornyCoeur) { object On extends ITelegramCommand { @@ -18,7 +18,7 @@ object MornyHellos { override val description: String = "检查是否在线" override def execute (using command: InputCommand, event: Update): Unit = - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_ONLINE_STATUS_RETURN ).replyToMessageId(event.message.messageId) @@ -33,7 +33,7 @@ object MornyHellos { override val description: String = "打招呼" override def execute (using command: InputCommand, event: Update): Unit = - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_HELLO ).replyToMessageId(event.message.messageId) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala index 8a9b101..038754a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnStart.scala @@ -1,6 +1,7 @@ package cc.sukazyo.cono.morny.bot.command import cc.sukazyo.cono.morny.MornyCoeur +import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML} import cc.sukazyo.cono.morny.util.tgapi.InputCommand import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode @@ -8,22 +9,22 @@ import com.pengrad.telegrambot.request.SendPhoto import scala.language.postfixOps -object MornyInfoOnStart extends ISimpleCommand { +class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand { override val name: String = "start" override val aliases: Array[ICommandAlias] | Null = null override def execute (using command: InputCommand, event: Update): Unit = { - MornyCoeur.extra exec new SendPhoto( + coeur.extra exec new SendPhoto( event.message.chat.id, - MornyInformation.getAboutPic + getAboutPic ).caption( s"""欢迎使用 Morny Cono来自安妮的侍从小鼠。 |Morny 具有各种各样的功能。 | |———————————————— - |${MornyInformation.getMornyAboutLinksHTML} + |$getMornyAboutLinksHTML |———————————————— | |(你可以随时通过 /info 重新获得这些信息)""" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala index 8a55bc7..583b885 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala @@ -1,7 +1,8 @@ package cc.sukazyo.cono.morny.bot.command -import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornyCoeur, MornySystem} -import cc.sukazyo.cono.morny.data.{TelegramImages, TelegramStickers} +import cc.sukazyo.cono.morny.{BuildConfig, MornyCoeur, MornySystem} +import cc.sukazyo.cono.morny.data.MornyInformation.* +import cc.sukazyo.cono.morny.data.TelegramStickers 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 @@ -10,12 +11,10 @@ import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker} import java.lang.System -import java.net.InetAddress -import java.rmi.UnknownHostException import scala.language.postfixOps // todo: maybe move some utils method outside -object MornyInformation extends ITelegramCommand { +class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand { private case object Subs { val STICKERS = "stickers" @@ -47,43 +46,8 @@ object MornyInformation extends ITelegramCommand { } - //noinspection ScalaWeakerAccess - def getVersionGitTagHTML: String = { - if (!MornySystem.isGitBuild) return "" - val g = StringBuilder() - val cm = BuildConfig.COMMIT substring(0, 8) - val cp = MornySystem.currentCodePath - if (cp == null) g++= s"$cm" - else g++= s"$cm" - if (!MornySystem.isCleanBuild) g++= ".δ" - g toString - } - - def getVersionAllFullTagHTML: String = { - val v = StringBuilder() - v ++= s"${MornySystem VERSION_BASE}" - if (MornySystem isUseDelta) v++=s"-δ${MornySystem VERSION_DELTA}" - if (MornySystem isGitBuild) v++="+git."++=getVersionGitTagHTML - v ++= s"*${MornySystem.CODENAME toUpperCase}" - v toString - } - - //noinspection ScalaWeakerAccess - def getRuntimeHostname: String|Null = { - try InetAddress.getLocalHost.getHostName - catch case _:UnknownHostException => null - } - - def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get - - def getMornyAboutLinksHTML: String = - s"""source code | backup - |反馈 / issue tracker - |使用说明书 / user guide & docs""" - .stripMargin - private def echoInfo (chatId: Long, replyTo: Int): Unit = { - MornyCoeur.extra exec new SendPhoto( + coeur.extra exec new SendPhoto( chatId, getAboutPic ).caption( @@ -128,15 +92,15 @@ object MornyInformation 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 = MornyCoeur.extra exec send_mid + val result_send_mid = coeur.extra exec send_mid send_sticker.replyToMessageId(result_send_mid.message.messageId) - MornyCoeur.extra exec send_sticker + coeur.extra exec send_sticker } private[command] def echoVersion (using event: Update): Unit = { val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ${h(MornySystem.VERSION_DELTA)}" else "" val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else "" - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, // language=html s"""version: @@ -153,7 +117,7 @@ object MornyInformation extends ITelegramCommand { private[command] def echoRuntime (using event: Update): Unit = { def sysprop (p: String): String = System.getProperty(p) - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, /* language=html */ s"""system: @@ -171,16 +135,16 @@ object MornyInformation extends ITelegramCommand { |- ${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC] |- [${BuildConfig.CODE_TIMESTAMP}] |continuous: - |- ${h(formatDuration(System.currentTimeMillis - MornyCoeur.coeurStartTimestamp))} - |- [${System.currentTimeMillis - MornyCoeur.coeurStartTimestamp}] - |- ${h(formatDate(MornyCoeur.coeurStartTimestamp, 0))} - |- [${MornyCoeur.coeurStartTimestamp}]""" + |- ${h(formatDuration(System.currentTimeMillis - coeur.coeurStartTimestamp))} + |- [${System.currentTimeMillis - coeur.coeurStartTimestamp}] + |- ${h(formatDate(coeur.coeurStartTimestamp, 0))} + |- [${coeur.coeurStartTimestamp}]""" .stripMargin ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) } private def echo404 (using event: Update): Unit = - MornyCoeur.extra exec new SendSticker( + coeur.extra exec new SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala index cad2acf..60f94ec 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformationOlds.scala @@ -3,16 +3,16 @@ package cc.sukazyo.cono.morny.bot.command import cc.sukazyo.cono.morny.util.tgapi.InputCommand import com.pengrad.telegrambot.model.Update -object MornyInformationOlds { +class MornyInformationOlds (using base: MornyInformation) { object Version extends ISimpleCommand: override val name: String = "version" override val aliases: Array[ICommandAlias] | Null = null - override def execute (using command: InputCommand, event: Update): Unit = MornyInformation.echoVersion + override def execute (using command: InputCommand, event: Update): Unit = base.echoVersion object Runtime extends ISimpleCommand: override val name: String = "runtime" override val aliases: Array[ICommandAlias] | Null = null - override def execute (using command: InputCommand, event: Update): Unit = MornyInformation.echoRuntime + override def execute (using command: InputCommand, event: Update): Unit = base.echoRuntime } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala index 8800c9f..05e0bed 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyManagers.scala @@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.SendSticker import scala.language.postfixOps -object MornyManagers { +class MornyManagers (using coeur: MornyCoeur) { object Exit extends ITelegramCommand { @@ -24,23 +24,23 @@ object MornyManagers { val user = event.message.from - if (MornyCoeur.trusted isTrusted user.id) { + if (coeur.trusted isTrusted user.id) { - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_EXIT ).replyToMessageId(event.message.messageId) logger info s"Morny exited by user ${user toLogTag}" - MornyCoeur.exit(0, user) + coeur.exit(0, user) } else { - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) logger info s"403 exit caught from user ${user toLogTag}" - MornyReport.unauthenticatedAction("/exit", user) + coeur.daemons.reporter.unauthenticatedAction("/exit", user) } @@ -59,23 +59,23 @@ object MornyManagers { val user = event.message.from - if (MornyCoeur.trusted isTrusted user.id) { + if (coeur.trusted isTrusted user.id) { logger info s"call save from command by ${user toLogTag}" - MornyCoeur.callSaveData() - MornyCoeur.extra exec SendSticker( + coeur.saveDataAll() + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_SAVED ).replyToMessageId(event.message.messageId) } else { - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_403 ).replyToMessageId(event.message.messageId) logger info s"403 save caught from user ${user toLogTag}" - MornyReport.unauthenticatedAction("/save", user) + coeur.daemons.reporter.unauthenticatedAction("/save", user) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala index c859f14..c468f32 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyOldJrrp.scala @@ -7,8 +7,7 @@ import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage -import scala.language.postfixOps -object MornyOldJrrp extends ITelegramCommand { +class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand { override val name: String = "jrrp" override val aliases: Array[ICommandAlias] | Null = null @@ -25,7 +24,7 @@ object MornyOldJrrp extends ITelegramCommand { case _ => "..." import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, // language=html f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— $jrrp%.2f%% ${h(ending)}" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala index 32e6819..2e0162e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala @@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.{SendMessage, SendSticker} import java.io.IOException import scala.language.postfixOps -object Nbnhhsh extends ITelegramCommand { +class Nbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand { private val NBNHHSH_RESULT_HEAD_HTML = // language=html @@ -32,7 +32,7 @@ object Nbnhhsh extends ITelegramCommand { else null if (queryTarget == null) - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( event.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(event.message.messageId) @@ -67,13 +67,13 @@ object Nbnhhsh extends ITelegramCommand { logger debug s"**exec as ${_word.name}" } - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, message toString ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) } catch case e: IOException => { - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, s"""[Exception] in query: |${h(e.getMessage)} diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala index 8316b42..808628c 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala @@ -6,18 +6,17 @@ import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.request.SendMessage -import javax.annotation.Nonnull -import javax.annotation.Nullable +import javax.annotation.{Nonnull, Nullable} import scala.language.postfixOps -object Testing extends ISimpleCommand { +class Testing (using coeur: MornyCoeur) extends ISimpleCommand { override val name: String = "test" override val aliases: Array[ICommandAlias] | Null = null override def execute (using command: InputCommand, event: Update): Unit = { - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, // language=html "Just a TEST command." diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala index 109077e..c75038b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala @@ -12,7 +12,7 @@ import scala.annotation.unused import scala.language.postfixOps //noinspection NonAsciiCharacters -object 喵呜 { +class 喵呜 (using coeur: MornyCoeur) { object 抱抱 extends ISimpleCommand { override val name: String = "抱抱" @@ -48,7 +48,7 @@ object 喵呜 { override val paramRule: String = "" override val description: String = "抽取一个神秘盒子" override def execute (using command: InputCommand, event: Update): Unit = { - MornyCoeur.extra exec new SendSticker( + coeur.extra exec new SendSticker( event.message.chat.id, TelegramStickers ID_PROGYNOVA ).replyToMessageId(event.message.messageId) @@ -58,7 +58,7 @@ object 喵呜 { 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 - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, if (isNew) whileNew else whileRec ).replyToMessageId(target.messageId).parseMode(ParseMode HTML) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala index 5321657..5ef39f5 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala @@ -8,7 +8,7 @@ import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendMessage //noinspection NonAsciiCharacters -object 私わね extends ISimpleCommand { +class 私わね (using coeur: MornyCoeur) extends ISimpleCommand { override val name: String = "me" override val aliases: Array[ICommandAlias] | Null = null @@ -17,7 +17,7 @@ object 私わね extends ISimpleCommand { if ((1 over 521) chance_is true) { val text = "/打假" - MornyCoeur.extra exec new SendMessage( + coeur.extra exec new SendMessage( event.message.chat.id, text ).replyToMessageId(event.message.messageId) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala index 4f39ac7..ea3149b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyEventListeners.scala @@ -1,27 +1,21 @@ package cc.sukazyo.cono.morny.bot.event import cc.sukazyo.cono.morny.bot.api.EventListenerManager +import cc.sukazyo.cono.morny.MornyCoeur -object MornyEventListeners { +class MornyEventListeners (using manager: EventListenerManager) (using coeur: MornyCoeur) { - def registerAllEvents(): Unit = { - - EventListenerManager.register( - // ACTIVITY_RECORDER - OnUpdateTimestampOffsetLock, - // KUOHUANHUAN_NEED_SLEEP - OnTelegramCommand, - OnUniMeowTrigger, - OnUserRandom, - OnQuestionMarkReply, - OnUserSlashAction, - OnInlineQuery, - OnCallMe, - OnCallMsgSend, - OnMedicationNotifyApply, - OnEventHackHandle - ) - - } + manager.register( + // ACTIVITY_RECORDER + // KUOHUANHUAN_NEED_SLEEP + OnUniMeowTrigger(using coeur.commands), + OnUserRandom(), + OnQuestionMarkReply(), + OnUserSlashAction(), + OnCallMe(), + OnCallMsgSend(), + OnMedicationNotifyApply(), + OnEventHackHandle() + ) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala similarity index 82% rename from src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala rename to src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala index d4df473..90858bb 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnInlineQuery.scala @@ -11,11 +11,11 @@ import scala.collection.mutable.ListBuffer import scala.language.postfixOps import scala.reflect.ClassTag -object OnInlineQuery extends EventListener { +class MornyOnInlineQuery (using queryManager: MornyQueries) (using coeur: MornyCoeur) extends EventListener { override def onInlineQuery (using update: Update): Boolean = { - val results: List[InlineQueryUnit[_]] = MornyQueries query update + val results: List[InlineQueryUnit[_]] = queryManager query update var cacheTime = Int.MaxValue var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL @@ -28,7 +28,7 @@ object OnInlineQuery extends EventListener { if (results isEmpty) return false - MornyCoeur.extra exec AnswerInlineQuery( + coeur.extra exec AnswerInlineQuery( update.inlineQuery.id, resultAnswers toArray:_* ).cacheTime(cacheTime).isPersonal(isPersonal) true diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala similarity index 84% rename from src/main/scala/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.scala rename to src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala index a850f21..541070b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnTelegramCommand.scala @@ -7,7 +7,7 @@ import cc.sukazyo.cono.morny.bot.command.MornyCommands import cc.sukazyo.cono.morny.util.tgapi.InputCommand import com.pengrad.telegrambot.model.{Message, Update} -object OnTelegramCommand extends EventListener { +class MornyOnTelegramCommand (using commandManager: MornyCommands) (using coeur: MornyCoeur) extends EventListener { override def onMessage (using update: Update): Boolean = { @@ -22,12 +22,12 @@ object OnTelegramCommand extends EventListener { if (!(inputCommand.command matches "^\\w+$")) logger debug "not command" false - else if ((inputCommand.target ne null) && (inputCommand.target != MornyCoeur.username)) + else if ((inputCommand.target ne null) && (inputCommand.target != coeur.username)) logger debug "not morny command" false else logger debug "is command" - MornyCommands.execute(using inputCommand) + commandManager.execute(using inputCommand) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala similarity index 81% rename from src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.scala rename to src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala index a1978a8..89f09cd 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala @@ -4,10 +4,10 @@ import cc.sukazyo.cono.morny.bot.api.EventListener import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.model.Update -object OnUpdateTimestampOffsetLock extends EventListener { +class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener { private def isOutdated (timestamp: Int): Boolean = - timestamp < (MornyCoeur.config.eventOutdatedTimestamp/1000) + timestamp < (coeur.config.eventOutdatedTimestamp/1000) override def onMessage (using update: Update): Boolean = isOutdated(update.message.date) override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala index 07f4d96..c42caf9 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala @@ -10,9 +10,9 @@ import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, Se import scala.language.postfixOps -object OnCallMe extends EventListener { +class OnCallMe (using coeur: MornyCoeur) extends EventListener { - private val me = MornyCoeur.config.trustedMaster + private val me = coeur.config.trustedMaster override def onMessage (using update: Update): Boolean = { @@ -32,7 +32,7 @@ object OnCallMe extends EventListener { case _ => return false - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( update.message.chat.id, TelegramStickers ID_SENT ).replyToMessageId(update.message.messageId) @@ -41,7 +41,7 @@ object OnCallMe extends EventListener { } private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Unit = - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( me, s"""request $itemHTML |from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}""" @@ -51,11 +51,11 @@ object OnCallMe extends EventListener { private def requestLastDinner (req: Message): Unit = { var isAllowed = false var lastDinnerData: Message|Null = null - if (MornyCoeur.trusted isTrusted_dinnerReader req.from.id) { + if (coeur.trusted isTrusted_dinnerReader req.from.id) { // todo: have issues - // i dont want to test it anymore... it might be deprecated soon - lastDinnerData = (MornyCoeur.extra exec GetChat(MornyCoeur.config.dinnerChatId)).chat.pinnedMessage - val sendResp = MornyCoeur.extra exec ForwardMessage( + // i dont want to test it anymore... it might be deprecated soon + lastDinnerData = (coeur.extra exec GetChat(coeur.config.dinnerChatId)).chat.pinnedMessage + val sendResp = coeur.extra exec ForwardMessage( req.from.id, lastDinnerData.forwardFromChat.id, lastDinnerData.forwardFromMessageId @@ -63,7 +63,7 @@ object OnCallMe extends EventListener { import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration} import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue; - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( req.from.id, "on %s [UTC+8]\n- %s before".formatted( h(formatDate(lastDinner_dateMillis, 8)), @@ -72,7 +72,7 @@ object OnCallMe extends EventListener { ).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId) isAllowed = true } else { - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( req.from.id, TelegramStickers ID_403 ).replyToMessageId(req.messageId) @@ -87,6 +87,6 @@ object OnCallMe extends EventListener { private def requestCustom (message: Message): Unit = requestItem(message.from, "[???]") - MornyCoeur.extra exec ForwardMessage(me, message.chat.id, message.messageId) + coeur.extra exec ForwardMessage(me, message.chat.id, message.messageId) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala index 1065a29..7544c6b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.scala @@ -11,7 +11,7 @@ import scala.collection.mutable.ArrayBuffer import scala.language.postfixOps import scala.util.matching.Regex -object OnCallMsgSend extends EventListener { +class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener { private val REGEX_MSG_SENDREQ_DATA_HEAD: Regex = "^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$"r @@ -59,8 +59,8 @@ object OnCallMsgSend extends EventListener { if message.text eq null then return false if !(message.text startsWith "*msg") then return false - if (!(MornyCoeur.trusted isTrusted message.from.id)) - MornyCoeur.extra exec SendSticker( + if (!(coeur.trusted isTrusted message.from.id)) + coeur.extra exec SendSticker( message.chat.id, TelegramStickers ID_403 ).replyToMessageId(message.messageId) @@ -71,15 +71,15 @@ object OnCallMsgSend extends EventListener { if (message.replyToMessage eq null) return answer404 val messageToSend = MessageToSend from message.replyToMessage if ((messageToSend eq null) || (messageToSend.message eq null)) return answer404 - val sendResponse = MornyCoeur.account execute messageToSend.toSendMessage() + val sendResponse = coeur.account execute messageToSend.toSendMessage() if (sendResponse isOk) { - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( update.message.chat.id, TelegramStickers ID_SENT ).replyToMessageId(update.message.messageId) } else { - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, // language=html s"""${sendResponse.errorCode} FAILED @@ -104,7 +104,7 @@ object OnCallMsgSend extends EventListener { if _toSend eq null then return answer404 else _toSend - val targetChatResponse = MornyCoeur.account execute GetChat(messageToSend.targetId) + val targetChatResponse = coeur.account execute GetChat(messageToSend.targetId) if (targetChatResponse isOk) { def getChatDescriptionHTML (chat: Chat): String = import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* @@ -113,12 +113,12 @@ object OnCallMsgSend 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 - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, getChatDescriptionHTML(targetChatResponse.chat) ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) } else { - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, // language=html s"""${targetChatResponse.errorCode} FAILED @@ -128,10 +128,10 @@ object OnCallMsgSend extends EventListener { } if messageToSend.message eq null then return true - val testSendResponse = MornyCoeur.account execute messageToSend.toSendMessage(update.message.chat.id) + val testSendResponse = coeur.account execute messageToSend.toSendMessage(update.message.chat.id) .replyToMessageId(update.message.messageId) if (!(testSendResponse isOk)) - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, // language=html s"""${testSendResponse.errorCode} FAILED @@ -144,7 +144,7 @@ object OnCallMsgSend extends EventListener { } private def answer404 (using update: Update): Boolean = - MornyCoeur.extra exec SendSticker( + coeur.extra exec SendSticker( update.message.chat.id, TelegramStickers ID_404 ).replyToMessageId(update.message.messageId) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala index f42d9e9..d6bc1f6 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnEventHackHandle.scala @@ -11,69 +11,37 @@ import com.pengrad.telegrambot.request.SendMessage import scala.collection.mutable import scala.language.postfixOps -object OnEventHackHandle extends EventListener { +class OnEventHackHandle (using coeur: MornyCoeur) extends EventListener { - private case class Hacker (from_chat: Long, from_message: Long): - override def toString: String = s"$from_chat/$from_message" - enum HackType: - case USER - case GROUP - case ANY - - private val hackers = mutable.HashMap.empty[String, Hacker] - - def registerHack (from_message: Long, from_user: Long, from_chat: Long, t: HackType): Unit = - val record = t match - case HackType.USER => s"(($from_user))" - case HackType.GROUP => s"{{$from_chat}}" - case HackType.ANY => "[[]]" - hackers += (record -> Hacker(from_chat, from_message)) - logger debug s"add hacker track $record" - - private def onEventHacked (chat: Long, fromUser: Long)(using update: Update): Boolean = { - 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" - import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h - MornyCoeur.extra exec SendMessage( - x.from_chat, - // language=html - s"${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}" - ).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt) - true - } + import coeur.daemons.eventHack.trigger override def onMessage (using update: Update): Boolean = - onEventHacked(update.message.chat.id, update.message.from.id) + trigger(update.message.chat.id, update.message.from.id) override def onEditedMessage (using update: Update): Boolean = - onEventHacked(update.editedMessage.chat.id, update.editedMessage.from.id) + trigger(update.editedMessage.chat.id, update.editedMessage.from.id) override def onChannelPost (using update: Update): Boolean = - onEventHacked(update.channelPost.chat.id, 0) + trigger(update.channelPost.chat.id, 0) override def onEditedChannelPost (using update: Update): Boolean = - onEventHacked(update.editedChannelPost.chat.id, 0) + trigger(update.editedChannelPost.chat.id, 0) override def onInlineQuery (using update: Update): Boolean = - onEventHacked(0, update.inlineQuery.from.id) + trigger(0, update.inlineQuery.from.id) override def onChosenInlineResult (using update: Update): Boolean = - onEventHacked(0, update.chosenInlineResult.from.id) + trigger(0, update.chosenInlineResult.from.id) override def onCallbackQuery (using update: Update): Boolean = - onEventHacked(0, update.callbackQuery.from.id) + trigger(0, update.callbackQuery.from.id) override def onShippingQuery (using update: Update): Boolean = - onEventHacked(0, update.shippingQuery.from.id) + trigger(0, update.shippingQuery.from.id) override def onPreCheckoutQuery (using update: Update): Boolean = - onEventHacked(0, update.preCheckoutQuery.from.id) + trigger(0, update.preCheckoutQuery.from.id) override def onPoll (using update: Update): Boolean = - onEventHacked(0, 0) + trigger(0, 0) override def onPollAnswer (using update: Update): Boolean = - onEventHacked(0, update.pollAnswer.user.id) + trigger(0, update.pollAnswer.user.id) override def onMyChatMemberUpdated (using update: Update): Boolean = - onEventHacked(update.myChatMember.chat.id, update.myChatMember.from.id) + trigger(update.myChatMember.chat.id, update.myChatMember.from.id) override def onChatMemberUpdated (using update: Update): Boolean = - onEventHacked(update.chatMember.chat.id, update.chatMember.from.id) + trigger(update.chatMember.chat.id, update.chatMember.from.id) override def onChatJoinRequest (using update: Update): Boolean = - onEventHacked(update.chatJoinRequest.chat.id, update.chatJoinRequest.from.id) + trigger(update.chatJoinRequest.chat.id, update.chatJoinRequest.from.id) } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala index 4e90c9b..25b29c1 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala @@ -5,7 +5,7 @@ import cc.sukazyo.cono.morny.MornyCoeur import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons} import com.pengrad.telegrambot.model.{Message, Update} -object OnMedicationNotifyApply extends EventListener { +class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener { override def onEditedMessage (using event: Update): Boolean = editedMessageProcess(event.editedMessage) @@ -13,8 +13,8 @@ object OnMedicationNotifyApply extends EventListener { editedMessageProcess(event.editedChannelPost) private def editedMessageProcess (edited: Message): Boolean = { - if edited.chat.id != MornyCoeur.config.medicationNotifyToChat then return false - MedicationTimer.refreshNotificationWrite(edited) + if edited.chat.id != coeur.config.medicationNotifyToChat then return false + coeur.daemons.medicationTimer.refreshNotificationWrite(edited) true } diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala index 3d73a09..9010871 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnQuestionMarkReply.scala @@ -2,21 +2,13 @@ package cc.sukazyo.cono.morny.bot.event import cc.sukazyo.cono.morny.bot.api.EventListener import cc.sukazyo.cono.morny.MornyCoeur +import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply.isAllMessageMark import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.request.SendMessage import scala.language.postfixOps -object OnQuestionMarkReply extends EventListener { - - private val QUESTION_MARKS = Set('?', '?', '¿', '⁈', '⁇', '‽', '❔', '❓') - - def isAllMessageMark (using text: String): Boolean = { - var isAll = true - for (c <- text) - if !(QUESTION_MARKS contains c) then isAll = false - isAll - } +class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener { override def onMessage (using event: Update): Boolean = { @@ -27,7 +19,7 @@ object OnQuestionMarkReply extends EventListener { if (1 over 8) chance_is false then return false if !isAllMessageMark(using event.message.text) then return false - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( event.message.chat.id, event.message.text ).replyToMessageId(event.message.messageId) true @@ -35,3 +27,16 @@ object OnQuestionMarkReply extends EventListener { } } + +object OnQuestionMarkReply { + + private val QUESTION_MARKS = Set('?', '?', '¿', '⁈', '⁇', '‽', '❔', '❓') + + def isAllMessageMark (using text: String): Boolean = { + var isAll = true + for (c <- text) + if !(QUESTION_MARKS contains c) then isAll = false + isAll + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala index c482b63..d1c97da 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala @@ -3,15 +3,16 @@ package cc.sukazyo.cono.morny.bot.event import cc.sukazyo.cono.morny.bot.api.EventListener import cc.sukazyo.cono.morny.bot.command.MornyCommands import cc.sukazyo.cono.morny.util.tgapi.InputCommand +import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.model.Update -object OnUniMeowTrigger extends EventListener { +class OnUniMeowTrigger (using commands: MornyCommands) (using coeur: MornyCoeur) extends EventListener { override def onMessage (using update: Update): Boolean = { if update.message.text eq null then return false var ok = false - for ((name, command) <- MornyCommands.commands_uni) + for ((name, command) <- commands.commands_uni) val _name = "/"+name if (_name == update.message.text) command.execute(using InputCommand(_name)) diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala index e47f3ce..2237630 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala @@ -7,7 +7,7 @@ import com.pengrad.telegrambot.request.SendMessage import scala.language.postfixOps -object OnUserRandom extends EventListener { +class OnUserRandom (using coeur: MornyCoeur) extends EventListener { private val USER_OR_QUERY = "^(.+)(?:还是|or)(.+)$"r private val USER_IF_QUERY = "^(.+)(?:吗\\?|?|\\?|吗?)$"r @@ -30,7 +30,7 @@ object OnUserRandom extends EventListener { if result == null then return false - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, result ).replyToMessageId(update.message.messageId) true diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala index db80040..033054d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala @@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.SendMessage import scala.language.postfixOps -object OnUserSlashAction extends EventListener { +class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener { private val TG_FORMAT = "^\\w+(@\\w+)?$"r @@ -58,7 +58,7 @@ object OnUserSlashAction extends EventListener { origin else update.message.replyToMessage - MornyCoeur.extra exec SendMessage( + coeur.extra exec SendMessage( update.message.chat.id, "%s %s%s %s %s!".format( origin.sender_firstnameRefHTML, diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala index 70c4739..a7a2b99 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala @@ -1,10 +1,12 @@ package cc.sukazyo.cono.morny.bot.query -import javax.annotation.Nullable +import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.model.Update +import javax.annotation.Nullable + trait ITelegramQuery { def query (event: Update): List[InlineQueryUnit[_]] | Null -} \ No newline at end of file +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala index 0c06dc4..11c6a12 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala @@ -1,17 +1,18 @@ package cc.sukazyo.cono.morny.bot.query import cc.sukazyo.cono.morny.bot.query +import cc.sukazyo.cono.morny.MornyCoeur import com.pengrad.telegrambot.model.Update import scala.collection.mutable.ListBuffer -object MornyQueries { +class MornyQueries (using MornyCoeur) { private val queryInstances = Set[ITelegramQuery]( - RawText, - MyInformation, - ShareToolTwitter, - ShareToolBilibili + RawText(), + MyInformation(), + ShareToolTwitter(), + ShareToolBilibili() ) def query (event: Update): List[InlineQueryUnit[_]] = { diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala index 2c8aa58..1b27068 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala @@ -7,7 +7,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex import scala.language.postfixOps -object MyInformation extends ITelegramQuery { +class MyInformation extends ITelegramQuery { private val ID_PREFIX = "[morny/info/me]" private val TITLE = "My Account Information" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala index 4fe13c8..036fb1d 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala @@ -5,7 +5,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex import scala.language.postfixOps -object RawText extends ITelegramQuery { +class RawText extends ITelegramQuery { private val ID_PREFIX = "[morny/r/text]" private val TITLE = "Raw Text" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala index 7b2f750..a74aad2 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala @@ -9,7 +9,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex import scala.language.postfixOps import scala.util.matching.Regex -object ShareToolBilibili extends ITelegramQuery { +class ShareToolBilibili extends ITelegramQuery { private val TITLE_BILI_AV = "[bilibili] Share video / av" private val TITLE_BILI_BV = "[bilibili] Share video / BV" diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala index baff446..03ba0de 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala @@ -7,7 +7,7 @@ import com.pengrad.telegrambot.model.request.InlineQueryResultArticle import scala.language.postfixOps import scala.util.matching.Regex -object ShareToolTwitter extends ITelegramQuery { +class ShareToolTwitter extends ITelegramQuery { private val TITLE_VX = "[tweet] Share as VxTwitter" private val TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)" diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala new file mode 100644 index 0000000..b20e72d --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/EventHacker.scala @@ -0,0 +1,49 @@ +package cc.sukazyo.cono.morny.daemon + +import cc.sukazyo.cono.morny.Log.logger +import cc.sukazyo.cono.morny.MornyCoeur +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 scala.collection.mutable + +class EventHacker (using coeur: MornyCoeur) { + + private case class Hacker (from_chat: Long, from_message: Long): + override def toString: String = s"$from_chat/$from_message" + + enum HackType: + case USER + case GROUP + case ANY + + private val hackers = mutable.HashMap.empty[String, Hacker] + + def registerHack (from_message: Long, from_user: Long, from_chat: Long, t: HackType): Unit = + val record = t match + case HackType.USER => s"(($from_user))" + case HackType.GROUP => s"{{$from_chat}}" + case HackType.ANY => "[[]]" + hackers += (record -> Hacker(from_chat, from_message)) + 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))" + 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" + import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h + coeur.extra exec SendMessage( + x.from_chat, + // language=html + s"${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}" + ).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt) + true + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala index 644abe2..880b557 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala @@ -1,7 +1,8 @@ package cc.sukazyo.cono.morny.daemon -import cc.sukazyo.cono.morny.MornyCoeur import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} +import cc.sukazyo.cono.morny.MornyCoeur +import cc.sukazyo.cono.morny.daemon.MedicationTimer.calcNextRoutineTimestamp import com.pengrad.telegrambot.model.{Message, MessageEntity} import com.pengrad.telegrambot.request.{EditMessageText, SendMessage} import com.pengrad.telegrambot.response.SendResponse @@ -10,15 +11,15 @@ import java.time.{LocalDateTime, ZoneOffset} import scala.collection.mutable.ArrayBuffer import scala.language.implicitConversions -object MedicationTimer extends Thread { +class MedicationTimer (using coeur: MornyCoeur) extends Thread { private val NOTIFY_MESSAGE = "🍥⏲" private val DAEMON_THREAD_NAME_DEF = "MedicationTimer" - private val use_timeZone = MornyCoeur.config.medicationTimerUseTimezone + private val use_timeZone = coeur.config.medicationTimerUseTimezone import scala.jdk.CollectionConverters.SetHasAsScala - private val notify_atHour: Set[Int] = MornyCoeur.config.medicationNotifyAt.asScala.toSet.map(_.intValue) - private val notify_toChat = MornyCoeur.config.medicationNotifyToChat + private val notify_atHour: Set[Int] = coeur.config.medicationNotifyAt.asScala.toSet.map(_.intValue) + private val notify_toChat = coeur.config.medicationNotifyToChat this.setName(DAEMON_THREAD_NAME_DEF) @@ -42,17 +43,22 @@ object MedicationTimer extends Thread { s"""unexpected error occurred on NotificationTimer |${exceptionLog(e)}""" .stripMargin - MornyReport.exception(e) + coeur.daemons.reporter.exception(e) } logger info "Medication Timer stopped." } private def sendNotification(): Unit = { - val sendResponse: SendResponse = MornyCoeur.extra exec SendMessage(notify_toChat, NOTIFY_MESSAGE) + val sendResponse: SendResponse = coeur.extra exec SendMessage(notify_toChat, NOTIFY_MESSAGE) if sendResponse isOk then lastNotify_messageId = sendResponse.message.messageId else lastNotify_messageId = null } + @throws[InterruptedException | IllegalArgumentException] + private def waitToNextRoutine (): Unit = { + Thread sleep calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour) + } + def refreshNotificationWrite (edited: Message): Unit = { if lastNotify_messageId != (edited.messageId toInt) then return import cc.sukazyo.cono.morny.util.CommonFormat.formatDate @@ -60,7 +66,7 @@ object MedicationTimer extends Thread { 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) - MornyCoeur.extra exec EditMessageText( + coeur.extra exec EditMessageText( notify_toChat, edited.messageId, edited.text + s"\n-- $editTime --" @@ -68,23 +74,22 @@ object MedicationTimer extends Thread { lastNotify_messageId = null } +} + +object MedicationTimer { + @throws[IllegalArgumentException] private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = { if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set") var time = LocalDateTime.ofEpochSecond( - baseTimeMillis/1000, ((baseTimeMillis%1000)*1000*1000) toInt, + baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt, zone ).withMinute(0).withSecond(0).withNano(0) time = time plusHours 1 - while (!(notifyAt contains (time getHour))) { + while (!(notifyAt contains(time getHour))) { time = time plusHours 1 } (time toInstant zone) toEpochMilli } - @throws[InterruptedException | IllegalArgumentException] - private def waitToNextRoutine (): Unit = { - Thread sleep calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour) - } - } diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala index 95c9415..3cf175e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyDaemons.scala @@ -3,13 +3,17 @@ package cc.sukazyo.cono.morny.daemon import cc.sukazyo.cono.morny.Log.logger import cc.sukazyo.cono.morny.MornyCoeur -object MornyDaemons { +class MornyDaemons (using val coeur: MornyCoeur) { + + val medicationTimer: MedicationTimer = MedicationTimer() + val reporter: MornyReport = MornyReport() + val eventHack: EventHacker = EventHacker() def start (): Unit = { logger info "ALL Morny Daemons starting..." // TrackerDataManager.init(); - MedicationTimer.start() - MornyReport.onMornyLogin() + medicationTimer.start() + reporter.onMornyLogin() logger info "Morny Daemons started." } @@ -17,12 +21,12 @@ object MornyDaemons { def stop (): Unit = { logger.info("ALL Morny Daemons stopping...") // TrackerDataManager.DAEMON.interrupt(); - MedicationTimer.interrupt() + medicationTimer.interrupt() // TrackerDataManager.trackingLock.lock(); - try { MedicationTimer.join() } + try { medicationTimer.join() } catch case e: InterruptedException => e.printStackTrace(System.out) - MornyReport.onMornyExit(MornyCoeur.exitReason) + reporter.onMornyExit() logger.info("ALL Morny Daemons STOPPED.") } diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala index 7d39431..803bd2e 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala @@ -3,6 +3,7 @@ package cc.sukazyo.cono.morny.daemon import cc.sukazyo.cono.morny.{MornyCoeur, MornyConfig} import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.bot.command.MornyInformation +import cc.sukazyo.cono.morny.data.MornyInformation.getVersionAllFullTagHTML 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 @@ -12,14 +13,11 @@ import com.pengrad.telegrambot.model.User import com.pengrad.telegrambot.request.{BaseRequest, SendMessage} import com.pengrad.telegrambot.response.BaseResponse -object MornyReport { - - private def unsupported: Boolean = (!MornyCoeur.available) || (MornyCoeur.config.reportToChat == -1) +class MornyReport (using coeur: MornyCoeur) { private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = { - if unsupported then return try { - MornyCoeur.extra exec report + coeur.extra exec report } catch case e: EventRuntimeException.ActionFailed => { logger warn s"""cannot execute report to telegram: @@ -31,7 +29,6 @@ object MornyReport { } def exception (e: Throwable, description: String|Null = null): Unit = { - if unsupported then return def _tgErrFormat: String = e match case api: EventRuntimeException.ActionFailed => // language=html @@ -39,7 +36,7 @@ object MornyReport { .formatted(GsonBuilder().setPrettyPrinting().create.toJson(api.response)) case _ => "" executeReport(SendMessage( - MornyCoeur.config.reportToChat, + coeur.config.reportToChat, // language=html s"""▌Coeur Unexpected Exception |${if description ne null then h(description)+"\n" else ""} @@ -49,9 +46,8 @@ object MornyReport { } def unauthenticatedAction (action: String, user: User): Unit = { - if unsupported then return executeReport(SendMessage( - MornyCoeur.config.reportToChat, + coeur.config.reportToChat, // language=html s"""▌User unauthenticated action |action: ${h(action)} @@ -62,14 +58,14 @@ object MornyReport { def onMornyLogin(): Unit = { executeReport(SendMessage( - MornyCoeur.config.reportToChat, + coeur.config.reportToChat, // language=html s"""▌Morny Logged in - |-v ${MornyInformation.getVersionAllFullTagHTML} - |as user ${MornyCoeur.username} + |-v $getVersionAllFullTagHTML + |as user ${coeur.username} | |as config fields: - |${sectionConfigFields(MornyCoeur.config)}""" + |${sectionConfigFields(coeur.config)}""" .stripMargin ).parseMode(ParseMode HTML)) } @@ -103,17 +99,16 @@ object MornyReport { echo dropRight 1 toString } - def onMornyExit (causedBy: AnyRef|Null): Unit = { - if unsupported then return - val causedTag = causedBy match + def onMornyExit (): Unit = { + val causedTag = coeur.exitReason match case u: User => u.fullnameRefHTML case n if n == null => "UNKNOWN reason" case a: AnyRef => /*language=html*/ s"${h(a.toString)}" executeReport(SendMessage( - MornyCoeur.config.reportToChat, + coeur.config.reportToChat, // language=html s"""▌Morny Exited - |from user @${MornyCoeur.username} + |from user @${coeur.username} | |by: $causedTag""" .stripMargin diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala new file mode 100644 index 0000000..12e7671 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/data/MornyInformation.scala @@ -0,0 +1,45 @@ +package cc.sukazyo.cono.morny.data + +import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornySystem} + +import java.net.InetAddress +import java.rmi.UnknownHostException + +object MornyInformation { + + //noinspection ScalaWeakerAccess + def getVersionGitTagHTML: String = { + if (!MornySystem.isGitBuild) return "" + val g = StringBuilder() + val cm = BuildConfig.COMMIT substring(0, 8) + val cp = MornySystem.currentCodePath + if (cp == null) g ++= s"$cm" + else g ++= s"$cm" + if (!MornySystem.isCleanBuild) g ++= ".δ" + g toString + } + + def getVersionAllFullTagHTML: String = { + val v = StringBuilder() + v ++= s"${MornySystem VERSION_BASE}" + if (MornySystem isUseDelta) v ++= s"-δ${MornySystem VERSION_DELTA}" + if (MornySystem isGitBuild) v ++= "+git." ++= getVersionGitTagHTML + v ++= s"*${MornySystem.CODENAME toUpperCase}" + v toString + } + + //noinspection ScalaWeakerAccess + def getRuntimeHostname: String | Null = { + try InetAddress.getLocalHost.getHostName + catch case _: UnknownHostException => null + } + + def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get + + def getMornyAboutLinksHTML: String = + s"""source code | backup + |反馈 / issue tracker + |使用说明书 / user guide & docs""" + .stripMargin + +} 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 667d5d3..6fb7757 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/data/TelegramImages.scala @@ -1,12 +1,13 @@ package cc.sukazyo.cono.morny.data +import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.MornyAssets +import cc.sukazyo.cono.morny.daemon.MornyReport +import cc.sukazyo.cono.morny.MornyAssets.AssetsException +import java.io.IOException import scala.language.postfixOps import scala.util.Using -import java.io.IOException -import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} -import cc.sukazyo.cono.morny.daemon.MornyReport object TelegramImages { @@ -14,19 +15,17 @@ object TelegramImages { private var cache: Array[Byte]|Null = _ + @throws[AssetsException] def get:Array[Byte] = if cache eq null then read() - if cache eq null then throw IllegalStateException("Failed to get assets file image.") cache + @throws[AssetsException] private def read (): Unit = { Using ((MornyAssets.pack getResource assetsPath)read) { stream => try { this.cache = stream.readAllBytes() } catch case e: IOException => { - logger error - s"""Cannot read resource file: - |${exceptionLog(e)}""".stripMargin - MornyReport.exception(e, "Cannot read resource file.") + throw AssetsException(e) } } }