diff --git a/gradle.properties b/gradle.properties
index ed12f09..24782ed 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
-VERSION = 1.3.0-dev5
+VERSION = 1.3.0-dev6
USE_DELTA = false
VERSION_DELTA =
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventEnv.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventEnv.scala
index 43a532f..9061a1f 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventEnv.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventEnv.scala
@@ -1,9 +1,11 @@
package cc.sukazyo.cono.morny.bot.api
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
+import cc.sukazyo.messiva.utils.StackUtils
import com.pengrad.telegrambot.model.Update
import scala.collection.mutable
+import scala.reflect.{classTag, ClassTag}
class EventEnv (
@@ -11,15 +13,34 @@ class EventEnv (
) {
- private var _isOk: Int = 0
+ trait StateSource (val from: StackTraceElement)
+ enum State:
+ case OK (_from: StackTraceElement) extends State with StateSource(_from)
+ case CANCELED (_from: StackTraceElement) extends State with StateSource(_from)
+
+ private val _status: mutable.ListBuffer[State] = mutable.ListBuffer.empty
private val variables: mutable.HashMap[Class[?], Any] = mutable.HashMap.empty
val timeStartup: EpochMillis = System.currentTimeMillis
- def isEventOk: Boolean = _isOk > 0
+ def isEventOk: Boolean = _status.lastOption match
+ case Some(x) if x == State.OK => true
+ case _ => false
//noinspection UnitMethodIsParameterless
def setEventOk: Unit =
- _isOk = _isOk + 1
+ _status += State.OK(StackUtils.getStackTrace(1)(1))
+
+ //noinspection UnitMethodIsParameterless
+ def setEventCanceled: Unit =
+ _status += State.CANCELED(StackUtils.getStackTrace(1)(1))
+
+ def state: State|Null =
+ _status.lastOption match
+ case Some(x) => x
+ case None => null
+
+ def status: List[State] =
+ _status.toList
def provide (i: Any): Unit =
variables += (i.getClass -> i)
@@ -30,6 +51,11 @@ class EventEnv (
case None => ConsumeResult(false)
}
+ def consume [T: ClassTag] (consumer: T => Unit): ConsumeResult =
+ variables get classTag[T].runtimeClass match
+ case Some(i) => consumer(i.asInstanceOf[T]); ConsumeResult(true)
+ case None => ConsumeResult(false)
+
class ConsumeResult (success: Boolean) {
def onfail (processor: => Unit): Unit = {
if !success then processor
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala
index 8834060..fa7c490 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListener.scala
@@ -17,8 +17,15 @@ trait EventListener () {
* if it should not run.
*/
def executeFilter (using env: EventEnv): Boolean =
- if env.isEventOk then false else true
+ if env.state == null then true else false
+ /** Run at all event listeners' listen methods done.
+ *
+ * Listen methods is the methods defined in [[EventListener this]]
+ * trait starts with `on`.
+ *
+ * This method will always run no matter the result of [[executeFilter]]
+ */
def atEventPost (using EventEnv): Unit = {}
def onMessage (using EventEnv): Unit = {}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala
index 9643578..7a810f4 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/api/EventListenerManager.scala
@@ -9,7 +9,6 @@ import com.pengrad.telegrambot.UpdatesListener
import scala.collection.mutable
import scala.language.postfixOps
-import scala.util.boundary
/** Contains a [[mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]].
*
@@ -30,59 +29,66 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
this setName s"upd-${update.updateId()}-$t"
override def run (): Unit = {
+
given env: EventEnv = EventEnv(update)
- boundary { for (i <- listeners) {
-
- if (i.executeFilter) try {
-
- updateThreadName("message")
- if update.message ne null then i.onMessage
- updateThreadName("edited-message")
- if update.editedMessage ne null then i.onEditedMessage
- updateThreadName("channel-post")
- if update.channelPost ne null then i.onChannelPost
- updateThreadName("edited-channel-post")
- if update.editedChannelPost ne null then i.onEditedChannelPost
- updateThreadName("inline-query")
- if update.inlineQuery ne null then i.onInlineQuery
- updateThreadName("chosen-inline-result")
- if update.chosenInlineResult ne null then i.onChosenInlineResult
- updateThreadName("callback-query")
- if update.callbackQuery ne null then i.onCallbackQuery
- updateThreadName("shipping-query")
- if update.shippingQuery ne null then i.onShippingQuery
- updateThreadName("pre-checkout-query")
- if update.preCheckoutQuery ne null then i.onPreCheckoutQuery
- updateThreadName("poll")
- if update.poll ne null then i.onPoll
- updateThreadName("poll-answer")
- if update.pollAnswer ne null then i.onPollAnswer
- updateThreadName("my-chat-member")
- if update.myChatMember ne null then i.onMyChatMemberUpdated
- updateThreadName("chat-member")
- if update.chatMember ne null then i.onChatMemberUpdated
- updateThreadName("chat-join-request")
- if update.chatJoinRequest ne null then i.onChatJoinRequest
-
- updateThreadName("#post")
- i.atEventPost
-
- } catch case e => {
- val errorMessage = StringBuilder()
- errorMessage ++= "Event throws unexpected exception:\n"
- errorMessage ++= (exceptionLog(e) indent 4)
- e match
- case actionFailed: EventRuntimeException.ActionFailed =>
- errorMessage ++= "\ntg-api action: response track: "
- errorMessage ++= (GsonBuilder().setPrettyPrinting().create().toJson(
- actionFailed.response
- ) indent 4) ++= "\n"
- case _ =>
- logger error errorMessage.toString
- coeur.daemons.reporter.exception(e, "on event running")
- }
-
- }}
+
+ for (i <- listeners)
+ if (i.executeFilter)
+ runEventListener(i)
+ for (i <- listeners)
+ runEventPost(i)
+
+ }
+
+ private def runEventPost (i: EventListener)(using EventEnv): Unit = {
+ updateThreadName("#post")
+ i.atEventPost
+ }
+
+ private def runEventListener (i: EventListener)(using EventEnv): Unit = {
+ try {
+ updateThreadName("message")
+ if update.message ne null then i.onMessage
+ updateThreadName("edited-message")
+ if update.editedMessage ne null then i.onEditedMessage
+ updateThreadName("channel-post")
+ if update.channelPost ne null then i.onChannelPost
+ updateThreadName("edited-channel-post")
+ if update.editedChannelPost ne null then i.onEditedChannelPost
+ updateThreadName("inline-query")
+ if update.inlineQuery ne null then i.onInlineQuery
+ updateThreadName("chosen-inline-result")
+ if update.chosenInlineResult ne null then i.onChosenInlineResult
+ updateThreadName("callback-query")
+ if update.callbackQuery ne null then i.onCallbackQuery
+ updateThreadName("shipping-query")
+ if update.shippingQuery ne null then i.onShippingQuery
+ updateThreadName("pre-checkout-query")
+ if update.preCheckoutQuery ne null then i.onPreCheckoutQuery
+ updateThreadName("poll")
+ if update.poll ne null then i.onPoll
+ updateThreadName("poll-answer")
+ if update.pollAnswer ne null then i.onPollAnswer
+ updateThreadName("my-chat-member")
+ if update.myChatMember ne null then i.onMyChatMemberUpdated
+ updateThreadName("chat-member")
+ if update.chatMember ne null then i.onChatMemberUpdated
+ updateThreadName("chat-join-request")
+ if update.chatJoinRequest ne null then i.onChatJoinRequest
+ } catch case e => {
+ val errorMessage = StringBuilder()
+ errorMessage ++= "Event throws unexpected exception:\n"
+ errorMessage ++= (exceptionLog(e) indent 4)
+ e match
+ case actionFailed: EventRuntimeException.ActionFailed =>
+ errorMessage ++= "\ntg-api action: response track: "
+ errorMessage ++= (GsonBuilder().setPrettyPrinting().create().toJson(
+ actionFailed.response
+ ) indent 4) ++= "\n"
+ case _ =>
+ logger error errorMessage.toString
+ coeur.daemons.reporter.exception(e, "on event running")
+ }
}
}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala
index ae44432..734af34 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/MornyOnUpdateTimestampOffsetLock.scala
@@ -2,13 +2,12 @@ package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
import cc.sukazyo.cono.morny.MornyCoeur
-import com.pengrad.telegrambot.model.Update
class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
private def checkOutdated (timestamp: Int)(using event: EventEnv): Unit =
if coeur.config.eventIgnoreOutdated && (timestamp < (coeur.coeurStartTimestamp/1000)) then
- event.setEventOk
+ event.setEventCanceled
override def onMessage (using event: EventEnv): Unit = checkOutdated(event.update.message.date)
override def onEditedMessage (using event: EventEnv): Unit = checkOutdated(event.update.editedMessage.date)
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
index 5dbbb9f..5d303d2 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
@@ -5,7 +5,7 @@ import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
import cc.sukazyo.cono.morny.data.TelegramStickers
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
-import com.pengrad.telegrambot.model.{Chat, Message, Update, User}
+import com.pengrad.telegrambot.model.{Chat, Message, User}
import com.pengrad.telegrambot.model.request.ParseMode
import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala
index ed24c3b..40f20c4 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnMedicationNotifyApply.scala
@@ -2,8 +2,7 @@ package cc.sukazyo.cono.morny.bot.event
import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
import cc.sukazyo.cono.morny.MornyCoeur
-import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons}
-import com.pengrad.telegrambot.model.{Message, Update}
+import com.pengrad.telegrambot.model.Message
class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
@@ -14,8 +13,8 @@ class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
private def editedMessageProcess (edited: Message)(using event: EventEnv): Unit = {
if edited.chat.id != coeur.config.medicationNotifyToChat then return;
- coeur.daemons.medicationTimer.refreshNotificationWrite(edited)
- event.setEventOk
+ if coeur.daemons.medicationTimer.refreshNotificationWrite(edited) then
+ event.setEventOk
}
}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala
index ccf140f..40e4fd3 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUniMeowTrigger.scala
@@ -4,13 +4,12 @@ import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
import cc.sukazyo.cono.morny.bot.command.MornyCommands
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
import cc.sukazyo.cono.morny.Log.logger
-import cc.sukazyo.cono.morny.MornyCoeur
-class OnUniMeowTrigger (using commands: MornyCommands) (using coeur: MornyCoeur) extends EventListener {
+class OnUniMeowTrigger (using commands: MornyCommands) extends EventListener {
override def onMessage (using event: EventEnv): Unit = {
- event.consume (classOf[InputCommand]) { input =>
+ event.consume[InputCommand] { input =>
logger trace s"got input command {$input} from event-context"
for ((name, command_instance) <- commands.commands_uni) {
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala
index 1171379..8275614 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MedicationTimer.scala
@@ -69,8 +69,8 @@ class MedicationTimer (using coeur: MornyCoeur) {
else lastNotify_messageId = None
}
- def refreshNotificationWrite (edited: Message): Unit = {
- if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return
+ def refreshNotificationWrite (edited: Message): Boolean = {
+ if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return false
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
val editTime = formatDate(edited.editDate*1000, use_timeZone.getTotalSeconds/60/60)
val entities = ArrayBuffer.empty[MessageEntity]
@@ -82,6 +82,7 @@ class MedicationTimer (using coeur: MornyCoeur) {
edited.text + s"\n-- $editTime --"
).entities(entities toArray:_*)
lastNotify_messageId = None
+ true
}
}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala
index a7ee9f9..307401a 100644
--- a/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala
+++ b/src/main/scala/cc/sukazyo/cono/morny/daemon/MornyReport.scala
@@ -134,10 +134,13 @@ class MornyReport (using coeur: MornyCoeur) {
object EventStatistics {
private var eventTotal = 0
+ private var eventCanceled = 0
private val runningTime: NumericStatistics[DurationMillis] = NumericStatistics()
def reset (): Unit = {
- eventTotal = 0; runningTime.reset()
+ eventTotal = 0
+ eventCanceled = 0
+ runningTime.reset()
}
private def runningTimeStatisticsHTML: String =
@@ -154,11 +157,13 @@ class MornyReport (using coeur: MornyCoeur) {
def eventStatisticsHTML: String =
import cc.sukazyo.cono.morny.util.UseMath.percentageOf as p
val processed = runningTime.count
- val ignored = eventTotal - processed
+ val canceled = eventCanceled
+ val ignored = eventTotal - processed - canceled
// language=html
s""" - total event received: $eventTotal
- | - event processed: (${eventTotal p processed}%
) $processed
| - event ignored: (${eventTotal p ignored}%
) $ignored
+ | - event canceled: (${eventTotal p canceled}%
) $canceled
+ | - event processed: (${eventTotal p processed}%
) $processed
| - processed time usage:
|${runningTimeStatisticsHTML.indent(3)}""".stripMargin
@@ -167,13 +172,23 @@ class MornyReport (using coeur: MornyCoeur) {
//noinspection ScalaWeakerAccess
case class EventTimeUsed (it: DurationMillis)
override def atEventPost (using event: EventEnv): Unit = {
+ import event.State
eventTotal += 1
- if event.isEventOk then {
- val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup)
- event provide timeUsed
- logger debug s"event consumed ${timeUsed.it}ms"
- runningTime ++ timeUsed.it
- }
+ event.state match
+ case State.OK(from) =>
+ val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup)
+ event provide timeUsed
+ logger debug
+ s"""event done with OK
+ | with time consumed ${timeUsed.it}ms
+ | by $from""".stripMargin
+ runningTime ++ timeUsed.it
+ case State.CANCELED(from) =>
+ eventCanceled += 1
+ logger debug
+ s"""event done with CANCELED"
+ | by $from""".stripMargin
+ case null =>
}
}