refactor runtime context from class instance to using/given

This commit is contained in:
A.C.Sukazyo Eyre 2023-09-23 16:38:07 +08:00
parent 92aa0e260e
commit 511036e2ce
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
50 changed files with 443 additions and 398 deletions

View File

@ -110,8 +110,6 @@ tasks.withType(ScalaCompile).configureEach {
scalaCompileOptions.encoding = proj_file_encoding.name() scalaCompileOptions.encoding = proj_file_encoding.name()
scalaCompileOptions.additionalParameters.add "-language:postfixOps" scalaCompileOptions.additionalParameters.add "-language:postfixOps"
// scalaCompileOptions.additionalParameters.add("-Yexplicit-nulls")
// scalaCompileOptions.additionalParameters.add "-language:experimental.saferExceptions"
} }
@ -196,10 +194,15 @@ publishing {
} }
} }
publications { publications {
//noinspection GroovyAssignabilityCheck
main (MavenPublication) { main (MavenPublication) {
//noinspection GroovyAssignabilityCheck
from components.java from components.java
//noinspection GroovyAssignabilityCheck
groupId = proj_group groupId = proj_group
//noinspection GroovyAssignabilityCheck
artifactId = proj_archive_name artifactId = proj_archive_name
//noinspection GroovyAssignabilityCheck
version = proj_version version = proj_version
} }
} }

View File

@ -8,7 +8,7 @@ MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
VERSION = 1.0.0-RC5 VERSION = 1.0.0-RC5
USE_DELTA = true USE_DELTA = true
VERSION_DELTA = scala1 VERSION_DELTA = scala2
CODENAME = beiping CODENAME = beiping

View File

@ -4,6 +4,8 @@ import cc.sukazyo.restools.ResourcesPackage
object MornyAssets { object MornyAssets {
class AssetsException (caused: Throwable) extends Exception("Cannot read assets file.", caused)
val pack: ResourcesPackage = ResourcesPackage(MornyAssets.getClass, "assets_morny") val pack: ResourcesPackage = ResourcesPackage(MornyAssets.getClass, "assets_morny")
} }

View File

@ -5,7 +5,8 @@ import cc.sukazyo.cono.morny.daemon.MornyDaemons
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
import cc.sukazyo.cono.morny.MornyCoeur.THREAD_MORNY_EXIT import cc.sukazyo.cono.morny.MornyCoeur.THREAD_MORNY_EXIT
import cc.sukazyo.cono.morny.bot.api.TelegramUpdatesListener 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 cc.sukazyo.cono.morny.util.tgapi.ExtraAction
import com.pengrad.telegrambot.TelegramBot import com.pengrad.telegrambot.TelegramBot
import com.pengrad.telegrambot.request.GetMe import com.pengrad.telegrambot.request.GetMe
@ -17,53 +18,15 @@ object MornyCoeur {
val THREAD_MORNY_EXIT = "morny-exiting" 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) { class MornyCoeur (using val config: MornyConfig) {
def my_config: MornyConfig = config
given MornyCoeur = this
///>>> BLOCK START instance configure & startup stage 1
logger info "Coeur starting..."
logger info s"args key:\n ${config.telegramBotKey}" logger info s"args key:\n ${config.telegramBotKey}"
if config.telegramBotUsername ne null then if config.telegramBotUsername ne null then
@ -76,13 +39,47 @@ class MornyCoeur (using config: MornyConfig) {
configure_exitCleanup() configure_exitCleanup()
///<<< BLOCK END instance configure & startup stage 1
/** [[TelegramBot]] account of this Morny */
val account: TelegramBot = __loginResult.account val account: TelegramBot = __loginResult.account
/** [[account]]'s telegram username */
val username: String = __loginResult.username val username: String = __loginResult.username
/** [[account]]'s telegram user id */
val userid: Long = __loginResult.userid 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 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 = { def saveDataAll(): Unit = {
// nothing to do // nothing to do
@ -90,15 +87,19 @@ class MornyCoeur (using config: MornyConfig) {
} }
private def exitCleanup (): Unit = { private def exitCleanup (): Unit = {
MornyDaemons.stop() daemons.stop()
if config.commandLogoutClear then if config.commandLogoutClear then
MornyCommands.automaticTGListRemove() commands.automaticTGListRemove()
} }
private def configure_exitCleanup (): Unit = { private def configure_exitCleanup (): Unit = {
Runtime.getRuntime.addShutdownHook(new Thread(() => exitCleanup(), THREAD_MORNY_EXIT)) 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 case class LoginResult(account: TelegramBot, username: String, userid: Long)
private def login (): LoginResult|Null = { private def login (): LoginResult|Null = {

View File

@ -86,7 +86,7 @@ public class MornyConfig {
/** /**
* morny 的事件忽略前缀时间<br> * morny 的事件忽略前缀时间<br>
* <br> * <br>
* {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock} * {@link cc.sukazyo.cono.morny.bot.event.MornyOnUpdateTimestampOffsetLock}
* 会根据这里定义的时间戳取消掉比此时间更早的事件链 * 会根据这里定义的时间戳取消掉比此时间更早的事件链
*/ */
public final long eventOutdatedTimestamp; public final long eventOutdatedTimestamp;

View File

@ -2,7 +2,6 @@ package cc.sukazyo.cono.morny
import cc.sukazyo.cono.morny.internal.BuildConfigField import cc.sukazyo.cono.morny.internal.BuildConfigField
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
import cc.sukazyo.cono.morny.daemon.MornyReport
import cc.sukazyo.cono.morny.util.FileUtils import cc.sukazyo.cono.morny.util.FileUtils
import java.io.IOException import java.io.IOException
@ -42,7 +41,7 @@ object MornySystem {
"<non-jar-runtime>" "<non-jar-runtime>"
case n: NoSuchAlgorithmException => case n: NoSuchAlgorithmException =>
logger error exceptionLog(n) logger error exceptionLog(n)
MornyReport.exception(n, "<coeur-md5/calculation-error>") // MornyReport.exception(n, "<coeur-md5/calculation-error>") // todo: will not implemented
"<calculation-error>" "<calculation-error>"
} }

View File

@ -143,7 +143,7 @@ object ServerMain {
Thread.currentThread setName THREAD_MORNY_INIT Thread.currentThread setName THREAD_MORNY_INIT
try try
MornyCoeur.init(using config build) MornyCoeur(using config build)
catch { catch {
case _: CheckFailure.NullTelegramBotKey => case _: CheckFailure.NullTelegramBotKey =>
logger.info("Parameter required has no value:\n --token.") logger.info("Parameter required has no value:\n --token.")

View File

@ -1,8 +1,9 @@
package cc.sukazyo.cono.morny.bot.api package cc.sukazyo.cono.morny.bot.api
import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
trait EventListener { trait EventListener (using MornyCoeur) {
def onMessage (using Update): Boolean = false def onMessage (using Update): Boolean = false
def onEditedMessage (using Update): Boolean = false def onEditedMessage (using Update): Boolean = false

View File

@ -1,8 +1,7 @@
package cc.sukazyo.cono.morny.bot.api 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.Log.{exceptionLog, logger}
import cc.sukazyo.cono.morny.daemon.MornyReport
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
@ -10,7 +9,7 @@ import com.pengrad.telegrambot.model.Update
import scala.collection.mutable import scala.collection.mutable
import scala.language.postfixOps import scala.language.postfixOps
object EventListenerManager { class EventListenerManager (using coeur: MornyCoeur) {
private val listeners = mutable.Queue.empty[EventListener] private val listeners = mutable.Queue.empty[EventListener]
@ -69,7 +68,7 @@ object EventListenerManager {
) indent 4) ++= "\n" ) indent 4) ++= "\n"
case _ => case _ =>
logger error errorMessage.toString logger error errorMessage.toString
MornyReport.exception(e, "on event running") coeur.daemons.reporter.exception(e, "on event running")
} }
if (status isOk) return if (status isOk) return
} }

View File

@ -1,16 +1,19 @@
package cc.sukazyo.cono.morny.bot.api package cc.sukazyo.cono.morny.bot.api
import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.UpdatesListener import com.pengrad.telegrambot.UpdatesListener
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
import java.util import java.util
import scala.jdk.CollectionConverters.* 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 = { override def process (updates: util.List[Update]): Int = {
for (update <- updates.asScala) for (update <- updates.asScala)
EventListenerManager.publishUpdate(using update) manager.publishUpdate(using update)
UpdatesListener.CONFIRMED_UPDATES_ALL UpdatesListener.CONFIRMED_UPDATES_ALL
} }

View File

@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.{DeleteMessage, GetChatMember, SendSticke
import scala.language.postfixOps import scala.language.postfixOps
object DirectMsgClear extends ISimpleCommand { class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand {
override val name: String = "r" override val name: String = "r"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -19,16 +19,16 @@ object DirectMsgClear extends ISimpleCommand {
logger debug "executing command /r" logger debug "executing command /r"
if (event.message.replyToMessage == null) return; if (event.message.replyToMessage == null) return;
logger trace "message is a reply" 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" logger trace "message replied is from me"
if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return; if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return;
logger trace "message is not outdated(48 hrs ago)" 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: // todo:
// it does not work. due to the Telegram Bot API doesn't provide // it does not work. due to the Telegram Bot API doesn't provide
// nested replyToMessage, so currently the trusted check by // nested replyToMessage, so currently the trusted check by
// replyToMessage.replyToMessage will not work! // replyToMessage.replyToMessage will not work!
def _isReplyTrusted: Boolean = def _isReplyTrusted: Boolean =
if (event.message.replyToMessage.replyToMessage == null) false if (event.message.replyToMessage.replyToMessage == null) false
else if (event.message.replyToMessage.replyToMessage.from.id == event.message.from.id) true else if (event.message.replyToMessage.replyToMessage.from.id == event.message.from.id) true
@ -36,19 +36,19 @@ object DirectMsgClear extends ISimpleCommand {
if (isTrusted || _isReplyTrusted) { if (isTrusted || _isReplyTrusted) {
MornyCoeur.extra exec DeleteMessage( coeur.extra exec DeleteMessage(
event.message.chat.id, event.message.replyToMessage.messageId event.message.chat.id, event.message.replyToMessage.messageId
) )
def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private
def _isPermission: Boolean = 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 .chatMember.canDeleteMessages
if (_isPrivate || _isPermission) { 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, event.message.chat.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)

View File

@ -2,7 +2,6 @@ package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.Log.logger import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur 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.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.util.CommonEncrypt import cc.sukazyo.cono.morny.util.CommonEncrypt
@ -17,7 +16,7 @@ import java.util.Base64
import scala.language.postfixOps import scala.language.postfixOps
/** Provides Telegram Command __`/encrypt`__. */ /** Provides Telegram Command __`/encrypt`__. */
object Encryptor extends ITelegramCommand { class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
override val name: String = "encrypt" override val name: String = "encrypt"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -48,7 +47,7 @@ object Encryptor extends ITelegramCommand {
val mod_uppercase = if (args.length > 1) { val mod_uppercase = if (args.length > 1) {
if (args.length < 3 && _is_mod_u(args(1))) true if (args.length < 3 && _is_mod_u(args(1))) true
else else
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -74,11 +73,11 @@ object Encryptor extends ITelegramCommand {
val _r = event.message.replyToMessage val _r = event.message.replyToMessage
if ((_r ne null) && (_r.document ne null)) { if ((_r ne null) && (_r.document ne null)) {
try {XFile( 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 _r.document.fileName
)} catch case e: IOException => )} catch case e: IOException =>
logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" 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 return
} else if ((_r ne null) && (_r.photo ne null)) { } else if ((_r ne null) && (_r.photo ne null)) {
try { try {
@ -92,22 +91,23 @@ object Encryptor extends ITelegramCommand {
if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.") if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.")
import cc.sukazyo.cono.morny.util.UseRandom.rand_id import cc.sukazyo.cono.morny.util.UseRandom.rand_id
XFile( 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" s"photo$rand_id.png"
) )
} catch } catch
case e: IOException => case e: IOException =>
//noinspection DuplicatedCode
logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}" logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}"
MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI") coeur.daemons.reporter.exception(e, "NetworkRequest error: TelegramFileAPI")
return return
case e: IllegalArgumentException => case e: IllegalArgumentException =>
logger warn s"FileProcess error: PhotoSize:\n\t${e.getMessage}" 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 return
} else if ((_r ne null) && (_r.text ne null)) { } else if ((_r ne null) && (_r.text ne null)) {
XText(_r.text) XText(_r.text)
} else { } else {
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
"<i><u>null</u></i>" "<i><u>null</u></i>"
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
@ -153,7 +153,7 @@ object Encryptor extends ITelegramCommand {
_tool_b64d.decode, _tool_b64d.decode,
CommonEncrypt.lint_base64FileName CommonEncrypt.lint_base64FileName
) } catch case _: IllegalArgumentException => ) } catch case _: IllegalArgumentException =>
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 // todo: is here better erro notify? TelegramStickers ID_404 // todo: is here better erro notify?
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -163,7 +163,7 @@ object Encryptor extends ITelegramCommand {
case "sha256" => genResult_hash(input, SHA256) case "sha256" => genResult_hash(input, SHA256)
case "sha512" => genResult_hash(input, SHA512) case "sha512" => genResult_hash(input, SHA512)
case _ => case _ =>
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -173,13 +173,13 @@ object Encryptor extends ITelegramCommand {
// output // output
result match result match
case _file: EXFile => case _file: EXFile =>
MornyCoeur.extra exec SendDocument( coeur.extra exec SendDocument(
event.message.chat.id, event.message.chat.id,
_file.result _file.result
).fileName(_file.resultName).replyToMessageId(event.message.messageId) ).fileName(_file.resultName).replyToMessageId(event.message.messageId)
case _text: EXTextLike => case _text: EXTextLike =>
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
s"<pre><code>${h(_text.text)}</code></pre>" s"<pre><code>${h(_text.text)}</code></pre>"
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
@ -211,7 +211,7 @@ object Encryptor extends ITelegramCommand {
* </blockquote> * </blockquote>
*/ */
private def echoHelp(chat: Long, replyTo: Int): Unit = private def echoHelp(chat: Long, replyTo: Int): Unit =
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
chat, chat,
s"""<b><u>base64</u></b>, b64 s"""<b><u>base64</u></b>, b64
|<b><u>base64url</u></b>, base64u, b64u |<b><u>base64url</u></b>, base64u, b64u

View File

@ -1,7 +1,5 @@
package cc.sukazyo.cono.morny.bot.command package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur 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.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
@ -9,7 +7,7 @@ import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps import scala.language.postfixOps
object EventHack extends ITelegramCommand { class EventHack (using coeur: MornyCoeur) extends ITelegramCommand {
override val name: String = "event_hack" override val name: String = "event_hack"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -18,15 +16,17 @@ object EventHack extends ITelegramCommand {
override def execute (using command: InputCommand, event: Update): Unit = { 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 "" val x_mode = if (command.args nonEmpty) command.args(0) else ""
def done_ok = def done_ok =
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_WAITING TelegramStickers ID_WAITING
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
def done_forbiddenForAny = def done_forbiddenForAny =
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -40,7 +40,7 @@ object EventHack extends ITelegramCommand {
) )
x_mode match x_mode match
case "any" => case "any" =>
if (MornyCoeur.trusted isTrusted event.message.from.id) if (coeur.trusted isTrusted event.message.from.id)
doRegister(HackType ANY) doRegister(HackType ANY)
done_ok done_ok
else done_forbiddenForAny else done_forbiddenForAny

View File

@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
import scala.language.postfixOps import scala.language.postfixOps
object GetUsernameAndId extends ITelegramCommand { class GetUsernameAndId (using coeur: MornyCoeur) extends ITelegramCommand {
override val name: String = "user" override val name: String = "user"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -21,7 +21,7 @@ object GetUsernameAndId extends ITelegramCommand {
val args = command.args val args = command.args
if (args.length > 1) if (args.length > 1)
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
"[Unavailable] Too much arguments." "[Unavailable] Too much arguments."
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -31,7 +31,7 @@ object GetUsernameAndId extends ITelegramCommand {
if (args nonEmpty) { if (args nonEmpty) {
try args(0) toLong try args(0) toLong
catch case e: NumberFormatException => catch case e: NumberFormatException =>
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
s"[Unavailable] ${e.getMessage}" s"[Unavailable] ${e.getMessage}"
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -39,10 +39,10 @@ object GetUsernameAndId extends ITelegramCommand {
} else if (event.message.replyToMessage eq null) event.message.from.id } else if (event.message.replyToMessage eq null) event.message.from.id
else event.message.replyToMessage.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) if (response.chatMember eq null)
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
"[Unavailable] user not found." "[Unavailable] user not found."
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -51,13 +51,13 @@ object GetUsernameAndId extends ITelegramCommand {
val user = response.chatMember.user val user = response.chatMember.user
if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID) if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID)
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
"<code>$__channel_identify</code>" "<code>$__channel_identify</code>"
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
return; return;
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
TelegramUserInformation getFormattedInformation user TelegramUserInformation getFormattedInformation user
).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML) ).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML)

View File

@ -9,7 +9,7 @@ import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps import scala.language.postfixOps
object IP186Query { class IP186Query (using coeur: MornyCoeur) {
private enum Subs (val cmd: String): private enum Subs (val cmd: String):
case IP extends Subs("ip") case IP extends Subs("ip")
@ -34,7 +34,7 @@ object IP186Query {
if (command.args isEmpty) if (command.args isEmpty)
if event.message.replyToMessage eq null then null else event.message.replyToMessage.text if event.message.replyToMessage eq null then null else event.message.replyToMessage.text
else if (command.args.length > 1) else if (command.args.length > 1)
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
"[Unavailable] Too much arguments." "[Unavailable] Too much arguments."
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -42,7 +42,7 @@ object IP186Query {
else command.args(0) else command.args(0)
if (target eq null) if (target eq null)
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
"[Unavailable] No ip defined." "[Unavailable] No ip defined."
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -57,7 +57,7 @@ object IP186Query {
case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target) case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target)
case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}") case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}")
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
s"""${h(response.url)} s"""${h(response.url)}
|<code>${h(response.body)}</code>""" |<code>${h(response.body)}</code>"""
@ -65,7 +65,7 @@ object IP186Query {
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
} catch case e: Exception => } catch case e: Exception =>
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message().chat().id(), event.message().chat().id(),
s"""[Exception] in query: s"""[Exception] in query:
|<code>${h(e.getMessage)}</code>""" |<code>${h(e.getMessage)}</code>"""

View File

@ -11,7 +11,7 @@ import scala.collection.{mutable, SeqMap}
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.language.postfixOps import scala.language.postfixOps
object MornyCommands { class MornyCommands (using coeur: MornyCoeur) {
private type CommandMap = SeqMap[String, ISimpleCommand] private type CommandMap = SeqMap[String, ISimpleCommand]
private def CommandMap (commands: ISimpleCommand*): CommandMap = private def CommandMap (commands: ISimpleCommand*): CommandMap =
@ -22,41 +22,47 @@ object MornyCommands {
stash += (alias.name -> i) stash += (alias.name -> i)
stash 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( private val commands: CommandMap = CommandMap(
MornyHellos.On, $MornyHellos.On,
MornyHellos.On, $MornyHellos.Hello,
MornyHellos.Hello, MornyInfoOnStart(),
MornyInfoOnStart, GetUsernameAndId(),
GetUsernameAndId, EventHack(),
EventHack, Nbnhhsh(),
Nbnhhsh, $IP186Query.IP,
IP186Query.IP, $IP186Query.Whois,
IP186Query.Whois, Encryptor(),
Encryptor, $MornyManagers.SaveData,
MornyManagers.SaveData, $MornyInformation,
MornyInformation, $MornyInformationOlds.Version,
MornyInformationOlds.Version, $MornyInformationOlds.Runtime,
MornyInformationOlds.Runtime, MornyOldJrrp(),
MornyOldJrrp, $MornyManagers.Exit,
MornyManagers.Exit,
Testing, Testing(),
DirectMsgClear, DirectMsgClear(),
//noinspection NonAsciiCharacters //noinspection NonAsciiCharacters
私わね, 私わね(),
//noinspection NonAsciiCharacters //noinspection NonAsciiCharacters
喵呜.Progynova $喵呜.Progynova
) )
//noinspection NonAsciiCharacters //noinspection NonAsciiCharacters
val commands_uni: CommandMap = CommandMap( val commands_uni: CommandMap = CommandMap(
喵呜.抱抱, $喵呜.抱抱,
喵呜.揉揉, $喵呜.揉揉,
喵呜.贴贴, $喵呜.贴贴,
喵呜.蹭蹭 $喵呜.蹭蹭
) )
def execute (using command: InputCommand, event: Update): Boolean = { def execute (using command: InputCommand, event: Update): Boolean = {
@ -69,7 +75,7 @@ object MornyCommands {
private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = { private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = {
if command.target eq null then false if command.target eq null then false
else else
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -79,14 +85,14 @@ object MornyCommands {
def automaticTGListUpdate (): Unit = { def automaticTGListUpdate (): Unit = {
val listing = commands_toTelegramList val listing = commands_toTelegramList
automaticTGListRemove() automaticTGListRemove()
MornyCoeur.extra exec SetMyCommands(listing:_*) coeur.extra exec SetMyCommands(listing:_*)
logger info logger info
s"""automatic updated telegram command list : s"""automatic updated telegram command list :
|${commandsTelegramList_toString(listing)}""".stripMargin |${commandsTelegramList_toString(listing)}""".stripMargin
} }
def automaticTGListRemove (): Unit = { def automaticTGListRemove (): Unit = {
MornyCoeur.extra exec DeleteMyCommands() coeur.extra exec DeleteMyCommands()
logger info "cleaned up command list" logger info "cleaned up command list"
} }

View File

@ -8,7 +8,7 @@ import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps import scala.language.postfixOps
object MornyHellos { class MornyHellos (using coeur: MornyCoeur) {
object On extends ITelegramCommand { object On extends ITelegramCommand {
@ -18,7 +18,7 @@ object MornyHellos {
override val description: String = "检查是否在线" override val description: String = "检查是否在线"
override def execute (using command: InputCommand, event: Update): Unit = override def execute (using command: InputCommand, event: Update): Unit =
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_ONLINE_STATUS_RETURN TelegramStickers ID_ONLINE_STATUS_RETURN
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -33,7 +33,7 @@ object MornyHellos {
override val description: String = "打招呼" override val description: String = "打招呼"
override def execute (using command: InputCommand, event: Update): Unit = override def execute (using command: InputCommand, event: Update): Unit =
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_HELLO TelegramStickers ID_HELLO
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)

View File

@ -1,6 +1,7 @@
package cc.sukazyo.cono.morny.bot.command package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.MornyCoeur import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML}
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.model.request.ParseMode
@ -8,22 +9,22 @@ import com.pengrad.telegrambot.request.SendPhoto
import scala.language.postfixOps import scala.language.postfixOps
object MornyInfoOnStart extends ISimpleCommand { class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand {
override val name: String = "start" override val name: String = "start"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
override def execute (using command: InputCommand, event: Update): Unit = { override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendPhoto( coeur.extra exec new SendPhoto(
event.message.chat.id, event.message.chat.id,
MornyInformation.getAboutPic getAboutPic
).caption( ).caption(
s"""欢迎使用 <b>Morny Cono</b><i>来自安妮的侍从小鼠</i>。 s"""欢迎使用 <b>Morny Cono</b><i>来自安妮的侍从小鼠</i>。
|Morny 具有各种各样的功能 |Morny 具有各种各样的功能
| |
| |
|${MornyInformation.getMornyAboutLinksHTML} |$getMornyAboutLinksHTML
| |
| |
|你可以随时通过 /info 重新获得这些信息""" |你可以随时通过 /info 重新获得这些信息"""

View File

@ -1,7 +1,8 @@
package cc.sukazyo.cono.morny.bot.command package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornyCoeur, MornySystem} import cc.sukazyo.cono.morny.{BuildConfig, MornyCoeur, MornySystem}
import cc.sukazyo.cono.morny.data.{TelegramImages, TelegramStickers} 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.CommonFormat.{formatDate, formatDuration}
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
@ -10,12 +11,10 @@ import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker} import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker}
import java.lang.System import java.lang.System
import java.net.InetAddress
import java.rmi.UnknownHostException
import scala.language.postfixOps import scala.language.postfixOps
// todo: maybe move some utils method outside // todo: maybe move some utils method outside
object MornyInformation extends ITelegramCommand { class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
private case object Subs { private case object Subs {
val STICKERS = "stickers" 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"<code>$cm</code>"
else g++= s"<a href='$cp'>$cm</a>"
if (!MornySystem.isCleanBuild) g++= ".<code>δ</code>"
g toString
}
def getVersionAllFullTagHTML: String = {
val v = StringBuilder()
v ++= s"<code>${MornySystem VERSION_BASE}</code>"
if (MornySystem isUseDelta) v++=s"-δ<code>${MornySystem VERSION_DELTA}</code>"
if (MornySystem isGitBuild) v++="+git."++=getVersionGitTagHTML
v ++= s"*<code>${MornySystem.CODENAME toUpperCase}</code>"
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"""<a href='${MornyAbout MORNY_SOURCECODE_LINK}'>source code</a> | <a href='${MornyAbout MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK}'>backup</a>
|<a href='${MornyAbout MORNY_ISSUE_TRACKER_LINK}'>反馈 / issue tracker</a>
|<a href='${MornyAbout MORNY_USER_GUIDE_LINK}'>使用说明书 / user guide & docs</a>"""
.stripMargin
private def echoInfo (chatId: Long, replyTo: Int): Unit = { private def echoInfo (chatId: Long, replyTo: Int): Unit = {
MornyCoeur.extra exec new SendPhoto( coeur.extra exec new SendPhoto(
chatId, chatId,
getAboutPic getAboutPic
).caption( ).caption(
@ -128,15 +92,15 @@ object MornyInformation extends ITelegramCommand {
val send_mid = SendMessage(send_chat, mid) val send_mid = SendMessage(send_chat, mid)
val send_sticker = SendSticker(send_chat, file_id) val send_sticker = SendSticker(send_chat, file_id)
if (send_replyTo != -1) send_mid.replyToMessageId(send_replyTo) 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) 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 = { private[command] def echoVersion (using event: Update): Unit = {
val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ<code>${h(MornySystem.VERSION_DELTA)}</code>" else "" val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ<code>${h(MornySystem.VERSION_DELTA)}</code>" else ""
val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else "" val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else ""
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
// language=html // language=html
s"""version: s"""version:
@ -153,7 +117,7 @@ object MornyInformation extends ITelegramCommand {
private[command] def echoRuntime (using event: Update): Unit = { private[command] def echoRuntime (using event: Update): Unit = {
def sysprop (p: String): String = System.getProperty(p) def sysprop (p: String): String = System.getProperty(p)
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
/* language=html */ /* language=html */
s"""system: s"""system:
@ -171,16 +135,16 @@ object MornyInformation extends ITelegramCommand {
|- <code>${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]</code> |- <code>${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]</code>
|- [<code>${BuildConfig.CODE_TIMESTAMP}</code>] |- [<code>${BuildConfig.CODE_TIMESTAMP}</code>]
|continuous: |continuous:
|- <code>${h(formatDuration(System.currentTimeMillis - MornyCoeur.coeurStartTimestamp))}</code> |- <code>${h(formatDuration(System.currentTimeMillis - coeur.coeurStartTimestamp))}</code>
|- [<code>${System.currentTimeMillis - MornyCoeur.coeurStartTimestamp}</code>] |- [<code>${System.currentTimeMillis - coeur.coeurStartTimestamp}</code>]
|- <code>${h(formatDate(MornyCoeur.coeurStartTimestamp, 0))}</code> |- <code>${h(formatDate(coeur.coeurStartTimestamp, 0))}</code>
|- [<code>${MornyCoeur.coeurStartTimestamp}</code>]""" |- [<code>${coeur.coeurStartTimestamp}</code>]"""
.stripMargin .stripMargin
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
} }
private def echo404 (using event: Update): Unit = private def echo404 (using event: Update): Unit =
MornyCoeur.extra exec new SendSticker( coeur.extra exec new SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)

View File

@ -3,16 +3,16 @@ package cc.sukazyo.cono.morny.bot.command
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
object MornyInformationOlds { class MornyInformationOlds (using base: MornyInformation) {
object Version extends ISimpleCommand: object Version extends ISimpleCommand:
override val name: String = "version" override val name: String = "version"
override val aliases: Array[ICommandAlias] | Null = null 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: object Runtime extends ISimpleCommand:
override val name: String = "runtime" override val name: String = "runtime"
override val aliases: Array[ICommandAlias] | Null = null 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
} }

View File

@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps import scala.language.postfixOps
object MornyManagers { class MornyManagers (using coeur: MornyCoeur) {
object Exit extends ITelegramCommand { object Exit extends ITelegramCommand {
@ -24,23 +24,23 @@ object MornyManagers {
val user = event.message.from 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, event.message.chat.id,
TelegramStickers ID_EXIT TelegramStickers ID_EXIT
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
logger info s"Morny exited by user ${user toLogTag}" logger info s"Morny exited by user ${user toLogTag}"
MornyCoeur.exit(0, user) coeur.exit(0, user)
} else { } else {
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
logger info s"403 exit caught from user ${user toLogTag}" 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 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}" logger info s"call save from command by ${user toLogTag}"
MornyCoeur.callSaveData() coeur.saveDataAll()
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_SAVED TelegramStickers ID_SAVED
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
} else { } else {
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
logger info s"403 save caught from user ${user toLogTag}" logger info s"403 save caught from user ${user toLogTag}"
MornyReport.unauthenticatedAction("/save", user) coeur.daemons.reporter.unauthenticatedAction("/save", user)
} }

View File

@ -7,8 +7,7 @@ import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand {
object MornyOldJrrp extends ITelegramCommand {
override val name: String = "jrrp" override val name: String = "jrrp"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -25,7 +24,7 @@ object MornyOldJrrp extends ITelegramCommand {
case _ => "..." case _ => "..."
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
// language=html // language=html
f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— <code>$jrrp%.2f%%</code> ${h(ending)}" f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— <code>$jrrp%.2f%%</code> ${h(ending)}"

View File

@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
import java.io.IOException import java.io.IOException
import scala.language.postfixOps import scala.language.postfixOps
object Nbnhhsh extends ITelegramCommand { class Nbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand {
private val NBNHHSH_RESULT_HEAD_HTML = private val NBNHHSH_RESULT_HEAD_HTML =
// language=html // language=html
@ -32,7 +32,7 @@ object Nbnhhsh extends ITelegramCommand {
else null else null
if (queryTarget == null) if (queryTarget == null)
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -67,13 +67,13 @@ object Nbnhhsh extends ITelegramCommand {
logger debug s"**exec as ${_word.name}" logger debug s"**exec as ${_word.name}"
} }
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
message toString message toString
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
} catch case e: IOException => { } catch case e: IOException => {
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
event.message.chat.id, event.message.chat.id,
s"""[Exception] in query: s"""[Exception] in query:
|${h(e.getMessage)} |${h(e.getMessage)}

View File

@ -6,18 +6,17 @@ import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.model.request.ParseMode import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.SendMessage import com.pengrad.telegrambot.request.SendMessage
import javax.annotation.Nonnull import javax.annotation.{Nonnull, Nullable}
import javax.annotation.Nullable
import scala.language.postfixOps import scala.language.postfixOps
object Testing extends ISimpleCommand { class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
override val name: String = "test" override val name: String = "test"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
override def execute (using command: InputCommand, event: Update): Unit = { override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
// language=html // language=html
"<b>Just</b> a TEST command." "<b>Just</b> a TEST command."

View File

@ -12,7 +12,7 @@ import scala.annotation.unused
import scala.language.postfixOps import scala.language.postfixOps
//noinspection NonAsciiCharacters //noinspection NonAsciiCharacters
object 喵呜 { class 喵呜 (using coeur: MornyCoeur) {
object 抱抱 extends ISimpleCommand { object 抱抱 extends ISimpleCommand {
override val name: String = "抱抱" override val name: String = "抱抱"
@ -48,7 +48,7 @@ object 喵呜 {
override val paramRule: String = "" override val paramRule: String = ""
override val description: String = "抽取一个神秘盒子" override val description: String = "抽取一个神秘盒子"
override def execute (using command: InputCommand, event: Update): Unit = { override def execute (using command: InputCommand, event: Update): Unit = {
MornyCoeur.extra exec new SendSticker( coeur.extra exec new SendSticker(
event.message.chat.id, event.message.chat.id,
TelegramStickers ID_PROGYNOVA TelegramStickers ID_PROGYNOVA
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
@ -58,7 +58,7 @@ object 喵呜 {
private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = { private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = {
val isNew = event.message.replyToMessage == null val isNew = event.message.replyToMessage == null
val target = if (isNew) event.message else event.message.replyToMessage val target = if (isNew) event.message else event.message.replyToMessage
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
if (isNew) whileNew else whileRec if (isNew) whileNew else whileRec
).replyToMessageId(target.messageId).parseMode(ParseMode HTML) ).replyToMessageId(target.messageId).parseMode(ParseMode HTML)

View File

@ -8,7 +8,7 @@ import com.pengrad.telegrambot.model.Update
import com.pengrad.telegrambot.request.SendMessage import com.pengrad.telegrambot.request.SendMessage
//noinspection NonAsciiCharacters //noinspection NonAsciiCharacters
object 私わね extends ISimpleCommand { class 私わね (using coeur: MornyCoeur) extends ISimpleCommand {
override val name: String = "me" override val name: String = "me"
override val aliases: Array[ICommandAlias] | Null = null override val aliases: Array[ICommandAlias] | Null = null
@ -17,7 +17,7 @@ object 私わね extends ISimpleCommand {
if ((1 over 521) chance_is true) { if ((1 over 521) chance_is true) {
val text = "/打假" val text = "/打假"
MornyCoeur.extra exec new SendMessage( coeur.extra exec new SendMessage(
event.message.chat.id, event.message.chat.id,
text text
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)

View File

@ -1,27 +1,21 @@
package cc.sukazyo.cono.morny.bot.event package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListenerManager 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 = { manager.register(
// ACTIVITY_RECORDER
EventListenerManager.register( // KUOHUANHUAN_NEED_SLEEP
// ACTIVITY_RECORDER OnUniMeowTrigger(using coeur.commands),
OnUpdateTimestampOffsetLock, OnUserRandom(),
// KUOHUANHUAN_NEED_SLEEP OnQuestionMarkReply(),
OnTelegramCommand, OnUserSlashAction(),
OnUniMeowTrigger, OnCallMe(),
OnUserRandom, OnCallMsgSend(),
OnQuestionMarkReply, OnMedicationNotifyApply(),
OnUserSlashAction, OnEventHackHandle()
OnInlineQuery, )
OnCallMe,
OnCallMsgSend,
OnMedicationNotifyApply,
OnEventHackHandle
)
}
} }

View File

@ -11,11 +11,11 @@ import scala.collection.mutable.ListBuffer
import scala.language.postfixOps import scala.language.postfixOps
import scala.reflect.ClassTag 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 = { 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 cacheTime = Int.MaxValue
var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL
@ -28,7 +28,7 @@ object OnInlineQuery extends EventListener {
if (results isEmpty) return false if (results isEmpty) return false
MornyCoeur.extra exec AnswerInlineQuery( coeur.extra exec AnswerInlineQuery(
update.inlineQuery.id, resultAnswers toArray:_* update.inlineQuery.id, resultAnswers toArray:_*
).cacheTime(cacheTime).isPersonal(isPersonal) ).cacheTime(cacheTime).isPersonal(isPersonal)
true true

View File

@ -7,7 +7,7 @@ import cc.sukazyo.cono.morny.bot.command.MornyCommands
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import com.pengrad.telegrambot.model.{Message, Update} 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 = { override def onMessage (using update: Update): Boolean = {
@ -22,12 +22,12 @@ object OnTelegramCommand extends EventListener {
if (!(inputCommand.command matches "^\\w+$")) if (!(inputCommand.command matches "^\\w+$"))
logger debug "not command" logger debug "not command"
false 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" logger debug "not morny command"
false false
else else
logger debug "is command" logger debug "is command"
MornyCommands.execute(using inputCommand) commandManager.execute(using inputCommand)
} }

View File

@ -4,10 +4,10 @@ import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
object OnUpdateTimestampOffsetLock extends EventListener { class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
private def isOutdated (timestamp: Int): Boolean = 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 onMessage (using update: Update): Boolean = isOutdated(update.message.date)
override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date) override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date)

View File

@ -10,9 +10,9 @@ import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, Se
import scala.language.postfixOps 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 = { override def onMessage (using update: Update): Boolean = {
@ -32,7 +32,7 @@ object OnCallMe extends EventListener {
case _ => case _ =>
return false return false
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
update.message.chat.id, update.message.chat.id,
TelegramStickers ID_SENT TelegramStickers ID_SENT
).replyToMessageId(update.message.messageId) ).replyToMessageId(update.message.messageId)
@ -41,7 +41,7 @@ object OnCallMe extends EventListener {
} }
private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Unit = private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Unit =
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
me, me,
s"""request $itemHTML s"""request $itemHTML
|from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}""" |from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}"""
@ -51,11 +51,11 @@ object OnCallMe extends EventListener {
private def requestLastDinner (req: Message): Unit = { private def requestLastDinner (req: Message): Unit = {
var isAllowed = false var isAllowed = false
var lastDinnerData: Message|Null = null var lastDinnerData: Message|Null = null
if (MornyCoeur.trusted isTrusted_dinnerReader req.from.id) { if (coeur.trusted isTrusted_dinnerReader req.from.id) {
// todo: have issues // todo: have issues
// i dont want to test it anymore... it might be deprecated soon // i dont want to test it anymore... it might be deprecated soon
lastDinnerData = (MornyCoeur.extra exec GetChat(MornyCoeur.config.dinnerChatId)).chat.pinnedMessage lastDinnerData = (coeur.extra exec GetChat(coeur.config.dinnerChatId)).chat.pinnedMessage
val sendResp = MornyCoeur.extra exec ForwardMessage( val sendResp = coeur.extra exec ForwardMessage(
req.from.id, req.from.id,
lastDinnerData.forwardFromChat.id, lastDinnerData.forwardFromChat.id,
lastDinnerData.forwardFromMessageId 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.CommonFormat.{formatDate, formatDuration}
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue; def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue;
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
req.from.id, req.from.id,
"<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>".formatted( "<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>".formatted(
h(formatDate(lastDinner_dateMillis, 8)), h(formatDate(lastDinner_dateMillis, 8)),
@ -72,7 +72,7 @@ object OnCallMe extends EventListener {
).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId)
isAllowed = true isAllowed = true
} else { } else {
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
req.from.id, req.from.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(req.messageId) ).replyToMessageId(req.messageId)
@ -87,6 +87,6 @@ object OnCallMe extends EventListener {
private def requestCustom (message: Message): Unit = private def requestCustom (message: Message): Unit =
requestItem(message.from, "<u>[???]</u>") requestItem(message.from, "<u>[???]</u>")
MornyCoeur.extra exec ForwardMessage(me, message.chat.id, message.messageId) coeur.extra exec ForwardMessage(me, message.chat.id, message.messageId)
} }

View File

@ -11,7 +11,7 @@ import scala.collection.mutable.ArrayBuffer
import scala.language.postfixOps import scala.language.postfixOps
import scala.util.matching.Regex 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 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 eq null then return false
if !(message.text startsWith "*msg") then return false if !(message.text startsWith "*msg") then return false
if (!(MornyCoeur.trusted isTrusted message.from.id)) if (!(coeur.trusted isTrusted message.from.id))
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
message.chat.id, message.chat.id,
TelegramStickers ID_403 TelegramStickers ID_403
).replyToMessageId(message.messageId) ).replyToMessageId(message.messageId)
@ -71,15 +71,15 @@ object OnCallMsgSend extends EventListener {
if (message.replyToMessage eq null) return answer404 if (message.replyToMessage eq null) return answer404
val messageToSend = MessageToSend from message.replyToMessage val messageToSend = MessageToSend from message.replyToMessage
if ((messageToSend eq null) || (messageToSend.message eq null)) return answer404 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) { if (sendResponse isOk) {
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
update.message.chat.id, update.message.chat.id,
TelegramStickers ID_SENT TelegramStickers ID_SENT
).replyToMessageId(update.message.messageId) ).replyToMessageId(update.message.messageId)
} else { } else {
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, update.message.chat.id,
// language=html // language=html
s"""<b><u>${sendResponse.errorCode} FAILED</u></b> s"""<b><u>${sendResponse.errorCode} FAILED</u></b>
@ -104,7 +104,7 @@ object OnCallMsgSend extends EventListener {
if _toSend eq null then return answer404 if _toSend eq null then return answer404
else _toSend else _toSend
val targetChatResponse = MornyCoeur.account execute GetChat(messageToSend.targetId) val targetChatResponse = coeur.account execute GetChat(messageToSend.targetId)
if (targetChatResponse isOk) { if (targetChatResponse isOk) {
def getChatDescriptionHTML (chat: Chat): String = def getChatDescriptionHTML (chat: Chat): String =
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
@ -113,12 +113,12 @@ object OnCallMsgSend extends EventListener {
s"""<i><u>${h(chat.id toString)}</u>@${h(chat.`type`.name)}</i>${if (chat.`type` != Chat.Type.Private) ":::" else ""} s"""<i><u>${h(chat.id toString)}</u>@${h(chat.`type`.name)}</i>${if (chat.`type` != Chat.Type.Private) ":::" else ""}
|${chat.typeTag} <b>${h(chat.safe_name)}</b> ${chat.safe_linkHTML}""" |${chat.typeTag} <b>${h(chat.safe_name)}</b> ${chat.safe_linkHTML}"""
.stripMargin .stripMargin
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, update.message.chat.id,
getChatDescriptionHTML(targetChatResponse.chat) getChatDescriptionHTML(targetChatResponse.chat)
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId) ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
} else { } else {
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, update.message.chat.id,
// language=html // language=html
s"""<b><u>${targetChatResponse.errorCode} FAILED</u></b> s"""<b><u>${targetChatResponse.errorCode} FAILED</u></b>
@ -128,10 +128,10 @@ object OnCallMsgSend extends EventListener {
} }
if messageToSend.message eq null then return true 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) .replyToMessageId(update.message.messageId)
if (!(testSendResponse isOk)) if (!(testSendResponse isOk))
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, update.message.chat.id,
// language=html // language=html
s"""<b><u>${testSendResponse.errorCode}</u> FAILED</b> s"""<b><u>${testSendResponse.errorCode}</u> FAILED</b>
@ -144,7 +144,7 @@ object OnCallMsgSend extends EventListener {
} }
private def answer404 (using update: Update): Boolean = private def answer404 (using update: Update): Boolean =
MornyCoeur.extra exec SendSticker( coeur.extra exec SendSticker(
update.message.chat.id, update.message.chat.id,
TelegramStickers ID_404 TelegramStickers ID_404
).replyToMessageId(update.message.messageId) ).replyToMessageId(update.message.messageId)

View File

@ -11,69 +11,37 @@ import com.pengrad.telegrambot.request.SendMessage
import scala.collection.mutable import scala.collection.mutable
import scala.language.postfixOps 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): import coeur.daemons.eventHack.trigger
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"<code>${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}</code>"
).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt)
true
}
override def onMessage (using update: Update): Boolean = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = override def onPoll (using update: Update): Boolean =
onEventHacked(0, 0) trigger(0, 0)
override def onPollAnswer (using update: Update): Boolean = 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 = 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 = 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 = 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)
} }

View File

@ -5,7 +5,7 @@ import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons} import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons}
import com.pengrad.telegrambot.model.{Message, Update} 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 = override def onEditedMessage (using event: Update): Boolean =
editedMessageProcess(event.editedMessage) editedMessageProcess(event.editedMessage)
@ -13,8 +13,8 @@ object OnMedicationNotifyApply extends EventListener {
editedMessageProcess(event.editedChannelPost) editedMessageProcess(event.editedChannelPost)
private def editedMessageProcess (edited: Message): Boolean = { private def editedMessageProcess (edited: Message): Boolean = {
if edited.chat.id != MornyCoeur.config.medicationNotifyToChat then return false if edited.chat.id != coeur.config.medicationNotifyToChat then return false
MedicationTimer.refreshNotificationWrite(edited) coeur.daemons.medicationTimer.refreshNotificationWrite(edited)
true true
} }

View File

@ -2,21 +2,13 @@ package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.EventListener import cc.sukazyo.cono.morny.bot.api.EventListener
import cc.sukazyo.cono.morny.MornyCoeur 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.model.Update
import com.pengrad.telegrambot.request.SendMessage import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps import scala.language.postfixOps
object OnQuestionMarkReply extends EventListener { class OnQuestionMarkReply (using coeur: MornyCoeur) 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
}
override def onMessage (using event: Update): Boolean = { 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 (1 over 8) chance_is false then return false
if !isAllMessageMark(using event.message.text) 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 event.message.chat.id, event.message.text
).replyToMessageId(event.message.messageId) ).replyToMessageId(event.message.messageId)
true 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
}
}

View File

@ -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.api.EventListener
import cc.sukazyo.cono.morny.bot.command.MornyCommands import cc.sukazyo.cono.morny.bot.command.MornyCommands
import cc.sukazyo.cono.morny.util.tgapi.InputCommand import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update 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 = { override def onMessage (using update: Update): Boolean = {
if update.message.text eq null then return false if update.message.text eq null then return false
var ok = false var ok = false
for ((name, command) <- MornyCommands.commands_uni) for ((name, command) <- commands.commands_uni)
val _name = "/"+name val _name = "/"+name
if (_name == update.message.text) if (_name == update.message.text)
command.execute(using InputCommand(_name)) command.execute(using InputCommand(_name))

View File

@ -7,7 +7,7 @@ import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps 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_OR_QUERY = "^(.+)(?:还是|or)(.+)$"r
private val USER_IF_QUERY = "^(.+)(?:吗\\?||\\?|吗?)$"r private val USER_IF_QUERY = "^(.+)(?:吗\\?||\\?|吗?)$"r
@ -30,7 +30,7 @@ object OnUserRandom extends EventListener {
if result == null then return false if result == null then return false
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, result update.message.chat.id, result
).replyToMessageId(update.message.messageId) ).replyToMessageId(update.message.messageId)
true true

View File

@ -11,7 +11,7 @@ import com.pengrad.telegrambot.request.SendMessage
import scala.language.postfixOps import scala.language.postfixOps
object OnUserSlashAction extends EventListener { class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
private val TG_FORMAT = "^\\w+(@\\w+)?$"r private val TG_FORMAT = "^\\w+(@\\w+)?$"r
@ -58,7 +58,7 @@ object OnUserSlashAction extends EventListener {
origin origin
else update.message.replyToMessage else update.message.replyToMessage
MornyCoeur.extra exec SendMessage( coeur.extra exec SendMessage(
update.message.chat.id, update.message.chat.id,
"%s %s%s %s %s!".format( "%s %s%s %s %s!".format(
origin.sender_firstnameRefHTML, origin.sender_firstnameRefHTML,

View File

@ -1,8 +1,10 @@
package cc.sukazyo.cono.morny.bot.query package cc.sukazyo.cono.morny.bot.query
import javax.annotation.Nullable import cc.sukazyo.cono.morny.MornyCoeur
import com.pengrad.telegrambot.model.Update import com.pengrad.telegrambot.model.Update
import javax.annotation.Nullable
trait ITelegramQuery { trait ITelegramQuery {
def query (event: Update): List[InlineQueryUnit[_]] | Null def query (event: Update): List[InlineQueryUnit[_]] | Null

View File

@ -1,17 +1,18 @@
package cc.sukazyo.cono.morny.bot.query package cc.sukazyo.cono.morny.bot.query
import 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 com.pengrad.telegrambot.model.Update
import scala.collection.mutable.ListBuffer import scala.collection.mutable.ListBuffer
object MornyQueries { class MornyQueries (using MornyCoeur) {
private val queryInstances = Set[ITelegramQuery]( private val queryInstances = Set[ITelegramQuery](
RawText, RawText(),
MyInformation, MyInformation(),
ShareToolTwitter, ShareToolTwitter(),
ShareToolBilibili ShareToolBilibili()
) )
def query (event: Update): List[InlineQueryUnit[_]] = { def query (event: Update): List[InlineQueryUnit[_]] = {

View File

@ -7,7 +7,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex
import scala.language.postfixOps import scala.language.postfixOps
object MyInformation extends ITelegramQuery { class MyInformation extends ITelegramQuery {
private val ID_PREFIX = "[morny/info/me]" private val ID_PREFIX = "[morny/info/me]"
private val TITLE = "My Account Information" private val TITLE = "My Account Information"

View File

@ -5,7 +5,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex
import scala.language.postfixOps import scala.language.postfixOps
object RawText extends ITelegramQuery { class RawText extends ITelegramQuery {
private val ID_PREFIX = "[morny/r/text]" private val ID_PREFIX = "[morny/r/text]"
private val TITLE = "Raw Text" private val TITLE = "Raw Text"

View File

@ -9,7 +9,7 @@ import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTex
import scala.language.postfixOps import scala.language.postfixOps
import scala.util.matching.Regex 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_AV = "[bilibili] Share video / av"
private val TITLE_BILI_BV = "[bilibili] Share video / BV" private val TITLE_BILI_BV = "[bilibili] Share video / BV"

View File

@ -7,7 +7,7 @@ import com.pengrad.telegrambot.model.request.InlineQueryResultArticle
import scala.language.postfixOps import scala.language.postfixOps
import scala.util.matching.Regex 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 = "[tweet] Share as VxTwitter"
private val TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)" private val TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)"

View File

@ -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"<code>${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}</code>"
).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt)
true
}
}

View File

@ -1,7 +1,8 @@
package cc.sukazyo.cono.morny.daemon 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.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.model.{Message, MessageEntity}
import com.pengrad.telegrambot.request.{EditMessageText, SendMessage} import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
import com.pengrad.telegrambot.response.SendResponse import com.pengrad.telegrambot.response.SendResponse
@ -10,15 +11,15 @@ import java.time.{LocalDateTime, ZoneOffset}
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.language.implicitConversions import scala.language.implicitConversions
object MedicationTimer extends Thread { class MedicationTimer (using coeur: MornyCoeur) extends Thread {
private val NOTIFY_MESSAGE = "🍥⏲" private val NOTIFY_MESSAGE = "🍥⏲"
private val DAEMON_THREAD_NAME_DEF = "MedicationTimer" 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 import scala.jdk.CollectionConverters.SetHasAsScala
private val notify_atHour: Set[Int] = MornyCoeur.config.medicationNotifyAt.asScala.toSet.map(_.intValue) private val notify_atHour: Set[Int] = coeur.config.medicationNotifyAt.asScala.toSet.map(_.intValue)
private val notify_toChat = MornyCoeur.config.medicationNotifyToChat private val notify_toChat = coeur.config.medicationNotifyToChat
this.setName(DAEMON_THREAD_NAME_DEF) this.setName(DAEMON_THREAD_NAME_DEF)
@ -42,17 +43,22 @@ object MedicationTimer extends Thread {
s"""unexpected error occurred on NotificationTimer s"""unexpected error occurred on NotificationTimer
|${exceptionLog(e)}""" |${exceptionLog(e)}"""
.stripMargin .stripMargin
MornyReport.exception(e) coeur.daemons.reporter.exception(e)
} }
logger info "Medication Timer stopped." logger info "Medication Timer stopped."
} }
private def sendNotification(): Unit = { 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 if sendResponse isOk then lastNotify_messageId = sendResponse.message.messageId
else lastNotify_messageId = null 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 = { def refreshNotificationWrite (edited: Message): Unit = {
if lastNotify_messageId != (edited.messageId toInt) then return if lastNotify_messageId != (edited.messageId toInt) then return
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
@ -60,7 +66,7 @@ object MedicationTimer extends Thread {
val entities = ArrayBuffer.empty[MessageEntity] val entities = ArrayBuffer.empty[MessageEntity]
if edited.entities ne null then entities ++= edited.entities if edited.entities ne null then entities ++= edited.entities
entities += MessageEntity(MessageEntity.Type.italic, edited.text.length + "\n-- ".length, editTime.length) entities += MessageEntity(MessageEntity.Type.italic, edited.text.length + "\n-- ".length, editTime.length)
MornyCoeur.extra exec EditMessageText( coeur.extra exec EditMessageText(
notify_toChat, notify_toChat,
edited.messageId, edited.messageId,
edited.text + s"\n-- $editTime --" edited.text + s"\n-- $editTime --"
@ -68,23 +74,22 @@ object MedicationTimer extends Thread {
lastNotify_messageId = null lastNotify_messageId = null
} }
}
object MedicationTimer {
@throws[IllegalArgumentException] @throws[IllegalArgumentException]
private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = { private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = {
if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set") if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set")
var time = LocalDateTime.ofEpochSecond( var time = LocalDateTime.ofEpochSecond(
baseTimeMillis/1000, ((baseTimeMillis%1000)*1000*1000) toInt, baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt,
zone zone
).withMinute(0).withSecond(0).withNano(0) ).withMinute(0).withSecond(0).withNano(0)
time = time plusHours 1 time = time plusHours 1
while (!(notifyAt contains (time getHour))) { while (!(notifyAt contains(time getHour))) {
time = time plusHours 1 time = time plusHours 1
} }
(time toInstant zone) toEpochMilli (time toInstant zone) toEpochMilli
} }
@throws[InterruptedException | IllegalArgumentException]
private def waitToNextRoutine (): Unit = {
Thread sleep calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour)
}
} }

View File

@ -3,13 +3,17 @@ package cc.sukazyo.cono.morny.daemon
import cc.sukazyo.cono.morny.Log.logger import cc.sukazyo.cono.morny.Log.logger
import cc.sukazyo.cono.morny.MornyCoeur 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 = { def start (): Unit = {
logger info "ALL Morny Daemons starting..." logger info "ALL Morny Daemons starting..."
// TrackerDataManager.init(); // TrackerDataManager.init();
MedicationTimer.start() medicationTimer.start()
MornyReport.onMornyLogin() reporter.onMornyLogin()
logger info "Morny Daemons started." logger info "Morny Daemons started."
} }
@ -17,12 +21,12 @@ object MornyDaemons {
def stop (): Unit = { def stop (): Unit = {
logger.info("ALL Morny Daemons stopping...") logger.info("ALL Morny Daemons stopping...")
// TrackerDataManager.DAEMON.interrupt(); // TrackerDataManager.DAEMON.interrupt();
MedicationTimer.interrupt() medicationTimer.interrupt()
// TrackerDataManager.trackingLock.lock(); // TrackerDataManager.trackingLock.lock();
try { MedicationTimer.join() } try { medicationTimer.join() }
catch case e: InterruptedException => catch case e: InterruptedException =>
e.printStackTrace(System.out) e.printStackTrace(System.out)
MornyReport.onMornyExit(MornyCoeur.exitReason) reporter.onMornyExit()
logger.info("ALL Morny Daemons STOPPED.") logger.info("ALL Morny Daemons STOPPED.")
} }

View File

@ -3,6 +3,7 @@ package cc.sukazyo.cono.morny.daemon
import cc.sukazyo.cono.morny.{MornyCoeur, MornyConfig} import cc.sukazyo.cono.morny.{MornyCoeur, MornyConfig}
import cc.sukazyo.cono.morny.Log.{exceptionLog, logger} import cc.sukazyo.cono.morny.Log.{exceptionLog, logger}
import cc.sukazyo.cono.morny.bot.command.MornyInformation 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.event.EventRuntimeException
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.* import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h import cc.sukazyo.cono.morny.util.tgapi.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.request.{BaseRequest, SendMessage}
import com.pengrad.telegrambot.response.BaseResponse import com.pengrad.telegrambot.response.BaseResponse
object MornyReport { class MornyReport (using coeur: MornyCoeur) {
private def unsupported: Boolean = (!MornyCoeur.available) || (MornyCoeur.config.reportToChat == -1)
private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = { private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = {
if unsupported then return
try { try {
MornyCoeur.extra exec report coeur.extra exec report
} catch case e: EventRuntimeException.ActionFailed => { } catch case e: EventRuntimeException.ActionFailed => {
logger warn logger warn
s"""cannot execute report to telegram: s"""cannot execute report to telegram:
@ -31,7 +29,6 @@ object MornyReport {
} }
def exception (e: Throwable, description: String|Null = null): Unit = { def exception (e: Throwable, description: String|Null = null): Unit = {
if unsupported then return
def _tgErrFormat: String = e match def _tgErrFormat: String = e match
case api: EventRuntimeException.ActionFailed => case api: EventRuntimeException.ActionFailed =>
// language=html // language=html
@ -39,7 +36,7 @@ object MornyReport {
.formatted(GsonBuilder().setPrettyPrinting().create.toJson(api.response)) .formatted(GsonBuilder().setPrettyPrinting().create.toJson(api.response))
case _ => "" case _ => ""
executeReport(SendMessage( executeReport(SendMessage(
MornyCoeur.config.reportToChat, coeur.config.reportToChat,
// language=html // language=html
s"""<b>▌Coeur Unexpected Exception </b> s"""<b>▌Coeur Unexpected Exception </b>
|${if description ne null then h(description)+"\n" else ""} |${if description ne null then h(description)+"\n" else ""}
@ -49,9 +46,8 @@ object MornyReport {
} }
def unauthenticatedAction (action: String, user: User): Unit = { def unauthenticatedAction (action: String, user: User): Unit = {
if unsupported then return
executeReport(SendMessage( executeReport(SendMessage(
MornyCoeur.config.reportToChat, coeur.config.reportToChat,
// language=html // language=html
s"""<b>▌User unauthenticated action</b> s"""<b>▌User unauthenticated action</b>
|action: ${h(action)} |action: ${h(action)}
@ -62,14 +58,14 @@ object MornyReport {
def onMornyLogin(): Unit = { def onMornyLogin(): Unit = {
executeReport(SendMessage( executeReport(SendMessage(
MornyCoeur.config.reportToChat, coeur.config.reportToChat,
// language=html // language=html
s"""<b>▌Morny Logged in</b> s"""<b>▌Morny Logged in</b>
|-v ${MornyInformation.getVersionAllFullTagHTML} |-v $getVersionAllFullTagHTML
|as user ${MornyCoeur.username} |as user ${coeur.username}
| |
|as config fields: |as config fields:
|${sectionConfigFields(MornyCoeur.config)}""" |${sectionConfigFields(coeur.config)}"""
.stripMargin .stripMargin
).parseMode(ParseMode HTML)) ).parseMode(ParseMode HTML))
} }
@ -103,17 +99,16 @@ object MornyReport {
echo dropRight 1 toString echo dropRight 1 toString
} }
def onMornyExit (causedBy: AnyRef|Null): Unit = { def onMornyExit (): Unit = {
if unsupported then return val causedTag = coeur.exitReason match
val causedTag = causedBy match
case u: User => u.fullnameRefHTML case u: User => u.fullnameRefHTML
case n if n == null => "UNKNOWN reason" case n if n == null => "UNKNOWN reason"
case a: AnyRef => /*language=html*/ s"<code>${h(a.toString)}</code>" case a: AnyRef => /*language=html*/ s"<code>${h(a.toString)}</code>"
executeReport(SendMessage( executeReport(SendMessage(
MornyCoeur.config.reportToChat, coeur.config.reportToChat,
// language=html // language=html
s"""<b>▌Morny Exited</b> s"""<b>▌Morny Exited</b>
|from user @${MornyCoeur.username} |from user @${coeur.username}
| |
|by: $causedTag""" |by: $causedTag"""
.stripMargin .stripMargin

View File

@ -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"<code>$cm</code>"
else g ++= s"<a href='$cp'>$cm</a>"
if (!MornySystem.isCleanBuild) g ++= ".<code>δ</code>"
g toString
}
def getVersionAllFullTagHTML: String = {
val v = StringBuilder()
v ++= s"<code>${MornySystem VERSION_BASE}</code>"
if (MornySystem isUseDelta) v ++= s"-δ<code>${MornySystem VERSION_DELTA}</code>"
if (MornySystem isGitBuild) v ++= "+git." ++= getVersionGitTagHTML
v ++= s"*<code>${MornySystem.CODENAME toUpperCase}</code>"
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"""<a href='${MornyAbout MORNY_SOURCECODE_LINK}'>source code</a> | <a href='${MornyAbout MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK}'>backup</a>
|<a href='${MornyAbout MORNY_ISSUE_TRACKER_LINK}'>反馈 / issue tracker</a>
|<a href='${MornyAbout MORNY_USER_GUIDE_LINK}'>使用说明书 / user guide & docs</a>"""
.stripMargin
}

View File

@ -1,12 +1,13 @@
package cc.sukazyo.cono.morny.data 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.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.language.postfixOps
import scala.util.Using 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 { object TelegramImages {
@ -14,19 +15,17 @@ object TelegramImages {
private var cache: Array[Byte]|Null = _ private var cache: Array[Byte]|Null = _
@throws[AssetsException]
def get:Array[Byte] = def get:Array[Byte] =
if cache eq null then read() if cache eq null then read()
if cache eq null then throw IllegalStateException("Failed to get assets file image.")
cache cache
@throws[AssetsException]
private def read (): Unit = { private def read (): Unit = {
Using ((MornyAssets.pack getResource assetsPath)read) { stream => Using ((MornyAssets.pack getResource assetsPath)read) { stream =>
try { this.cache = stream.readAllBytes() } try { this.cache = stream.readAllBytes() }
catch case e: IOException => { catch case e: IOException => {
logger error throw AssetsException(e)
s"""Cannot read resource file:
|${exceptionLog(e)}""".stripMargin
MornyReport.exception(e, "Cannot read resource file.")
} }
} }
} }