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 20fdd58..01f0e4b 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyCoeur.scala @@ -367,7 +367,6 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes * }}} */ object dsl extends BotExtension { - given coeur: MornyCoeur = MornyCoeur.this given account: TelegramBot = MornyCoeur.this.account given translations: MornyLangs = MornyCoeur.this.lang } diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/MornyModule.scala b/src/main/scala/cc/sukazyo/cono/morny/core/MornyModule.scala index 87971e6..3d3cd0a 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/MornyModule.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/MornyModule.scala @@ -4,10 +4,44 @@ import cc.sukazyo.cono.morny.core.MornyCoeur.* trait MornyModule { + /** The unique id of this Morny module. + * + * Requires to be unique between modules that will loaded, requires only contains ASCII + * characters excepts control characters. + * + * This module id will listed in the module lists as the primary key. Although currently + * is no checks to make sure this is unique (and if is legal), it is still highly + * recommended making this unique between modules, for forward compatibles and third-party + * compatibles. + * + * Recommended to be named using all lowercased characters and hyphen character (`-`) like + * `module-author-or-provider.module-name` . For example, the Morny internal module + * `morny_misc` has the id `morny.misc`, and `stickers_get` has the id `morny.stickers-get`. + */ val id: String + /** Human readable name of this Morny module. + * + * No specific formats requires except do not contains ASCII control characters. + * + * No special usage except showing in module table. + * + * Recommended set this to a English name while the i18n for this is not implemented yet. + */ val name: String + /** Version of this module build. + * + * No special usage except showing in module table. + */ val version: String + /** Human readable description of this Morny module. + * + * Should be a Markdown document. + * + * No special usage. + * + * Recommended to use English while the i18n for this is not implemented yet. + */ val description: String | Null def onInitializingPre (using MornyCoeur)(cxt: OnInitializingPreContext): Unit = {} diff --git a/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala b/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala index 50dad7d..e07beed 100644 --- a/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala +++ b/src/main/scala/cc/sukazyo/cono/morny/core/ServerModulesLoader.scala @@ -13,6 +13,7 @@ object ServerModulesLoader { morny.slash_action.ModuleSlashAction(), morny.nbnhhsh.ModuleNbnhhsh(), morny.ip186.ModuleIP186(), + morny.crons.ModuleCRONs(), morny.encrypt_tool.ModuleEncryptor(), morny.call_me.ModuleCallMe(), morny.social_share.ModuleSocialShare(), diff --git a/src/main/scala/cc/sukazyo/cono/morny/crons/ModuleCRONs.scala b/src/main/scala/cc/sukazyo/cono/morny/crons/ModuleCRONs.scala new file mode 100644 index 0000000..5d5e745 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/crons/ModuleCRONs.scala @@ -0,0 +1,24 @@ +package cc.sukazyo.cono.morny.crons + +import cc.sukazyo.cono.morny.core.internal.MornyInternalModule +import cc.sukazyo.cono.morny.core.MornyCoeur +import cc.sukazyo.cono.morny.crons.cmd.CommandCreate + +class ModuleCRONs extends MornyInternalModule { + + override val id: String = "morny.cron" + override val name: String = "Morny CRON messaging for Users" + override val description: String | Null = + // language=markdown + """Provides users to set CRON based timer for messaging to themselves. + | + |Not implemented yet while Morny's database is not implemented yet.""".stripMargin + + override def onInitializing (using MornyCoeur)(cxt: MornyCoeur.OnInitializingContext): Unit = { + import cxt.commandManager + + commandManager `register` CommandCreate() + + } + +} diff --git a/src/main/scala/cc/sukazyo/cono/morny/crons/cmd/CommandCreate.scala b/src/main/scala/cc/sukazyo/cono/morny/crons/cmd/CommandCreate.scala new file mode 100644 index 0000000..ca57a66 --- /dev/null +++ b/src/main/scala/cc/sukazyo/cono/morny/crons/cmd/CommandCreate.scala @@ -0,0 +1,81 @@ +package cc.sukazyo.cono.morny.crons.cmd + +import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand} +import cc.sukazyo.cono.morny.core.MornyCoeur +import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext} +import cc.sukazyo.cono.morny.data.TelegramStickers +import cc.sukazyo.cono.morny.util.tgapi.InputCommand +import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute +import cc.sukazyo.cono.morny.util.UseThrowable.toLogString +import com.cronutils.descriptor.CronDescriptor +import com.cronutils.model.{Cron, CronType} +import com.cronutils.model.definition.CronDefinitionBuilder +import com.cronutils.model.time.ExecutionTime +import com.cronutils.parser.CronParser +import com.pengrad.telegrambot.model.{Message, Update} +import com.pengrad.telegrambot.request.{SendMessage, SendSticker} + +import java.time.ZonedDateTime +import java.time.format.DateTimeFormatter + +class CommandCreate (using coeur: MornyCoeur) extends ISimpleCommand { + import coeur.dsl.{*, given} + + override val name: String = "create_cron_message" + override val aliases: List[ICommandAlias] = Nil + + override def execute (using command: InputCommand, event: Update): Unit = { + given context: MessagingContext.WithUserAndMessage = MessagingContext.extract(using event.message) + + SendMessage( + context.bind_chat.id, + "Type your CRON expression below:" + ).replyToMessageId(context.bind_message.messageId) + .unsafeExecute + + coeur.messageThreading.doAfter(execute1) + + } + + def execute1 (message: Message, prev: MessagingContext.WithUserAndMessage): Unit = { + given context: MessagingContext.WithUserAndMessage = MessagingContext.extract(using message) + + val cron: Cron = + try CronParser(CronDefinitionBuilder.instanceDefinitionFor(CronType.UNIX)) + .parse(message.text()) + catch case e: IllegalArgumentException => + ErrorMessage( + SendSticker( + context.bind_chat.id, + TelegramStickers.ID_404 + ), + SendMessage( + context.bind_chat.id, + s"""Probably you've entered an invalid CRON expression: + | + |${e.toLogString} + |""".stripMargin + ) + ).submit + return + + val cron_describe = CronDescriptor.instance().describe(cron) + val current = ZonedDateTime.now() + val cron_exec_time = ExecutionTime.forCron(cron) + val cron_next = cron_exec_time.nextExecution(current) + val cron_prev = cron_exec_time.lastExecution(current) + SendMessage( + context.bind_chat.id, + s"""Set CRON task. + | + |Your cron settings is runs $cron_describe + |Previous runs at: ${cron_prev.map(_.format(DateTimeFormatter.ISO_DATE_TIME)).orElse("Will not run before.")} + |Next will run at: ${cron_next.map(_.format(DateTimeFormatter.ISO_DATE_TIME)).orElse("Will not run afterwards.")} + | + |""".stripMargin + ).replyToMessageId(context.bind_message.messageId) + .unsafeExecute + + } + +}