mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-22 19:24:53 +08:00
for event, fix wrong OK stats, add CANCELED tag
- Now the status of EventEnv is a State array that infers the state history - State can be OK or CANCELED, and can be set multiple times - state method can get the last state set, and status method can get the state history - Default EventListener.executeFilter implementation is changed to true if stats is null - add consume[T](T=>Unit) for EventEnv, to simplifying the old consume[T](Class[T])(T=>Unit) - changed execution sort of EventListener in EventListenerManager. Now atEventPost method will be run after all events' normal listeners complete. - cha OnMedicationNotifyApply will only tag event as OK when the refresh function works (fixed part of the wrong OK state) - cha MornyOnUpdateTimestampOffsetLock tag event CANCELED but not OK to fix part of the wrong OK state
This commit is contained in:
parent
7ee4a0d3c5
commit
c5c6683459
@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
|
|||||||
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
||||||
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
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
|
USE_DELTA = false
|
||||||
VERSION_DELTA =
|
VERSION_DELTA =
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package cc.sukazyo.cono.morny.bot.api
|
package cc.sukazyo.cono.morny.bot.api
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||||
|
import cc.sukazyo.messiva.utils.StackUtils
|
||||||
import com.pengrad.telegrambot.model.Update
|
import com.pengrad.telegrambot.model.Update
|
||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
import scala.reflect.{classTag, ClassTag}
|
||||||
|
|
||||||
class EventEnv (
|
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
|
private val variables: mutable.HashMap[Class[?], Any] = mutable.HashMap.empty
|
||||||
val timeStartup: EpochMillis = System.currentTimeMillis
|
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
|
//noinspection UnitMethodIsParameterless
|
||||||
def setEventOk: Unit =
|
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 =
|
def provide (i: Any): Unit =
|
||||||
variables += (i.getClass -> i)
|
variables += (i.getClass -> i)
|
||||||
@ -30,6 +51,11 @@ class EventEnv (
|
|||||||
case None => ConsumeResult(false)
|
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) {
|
class ConsumeResult (success: Boolean) {
|
||||||
def onfail (processor: => Unit): Unit = {
|
def onfail (processor: => Unit): Unit = {
|
||||||
if !success then processor
|
if !success then processor
|
||||||
|
@ -17,8 +17,15 @@ trait EventListener () {
|
|||||||
* if it should not run.
|
* if it should not run.
|
||||||
*/
|
*/
|
||||||
def executeFilter (using env: EventEnv): Boolean =
|
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 atEventPost (using EventEnv): Unit = {}
|
||||||
|
|
||||||
def onMessage (using EventEnv): Unit = {}
|
def onMessage (using EventEnv): Unit = {}
|
||||||
|
@ -9,7 +9,6 @@ import com.pengrad.telegrambot.UpdatesListener
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
import scala.language.postfixOps
|
import scala.language.postfixOps
|
||||||
import scala.util.boundary
|
|
||||||
|
|
||||||
/** Contains a [[mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]].
|
/** Contains a [[mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]].
|
||||||
*
|
*
|
||||||
@ -30,11 +29,24 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
|
|||||||
this setName s"upd-${update.updateId()}-$t"
|
this setName s"upd-${update.updateId()}-$t"
|
||||||
|
|
||||||
override def run (): Unit = {
|
override def run (): Unit = {
|
||||||
|
|
||||||
given env: EventEnv = EventEnv(update)
|
given env: EventEnv = EventEnv(update)
|
||||||
boundary { for (i <- listeners) {
|
|
||||||
|
|
||||||
if (i.executeFilter) try {
|
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")
|
updateThreadName("message")
|
||||||
if update.message ne null then i.onMessage
|
if update.message ne null then i.onMessage
|
||||||
updateThreadName("edited-message")
|
updateThreadName("edited-message")
|
||||||
@ -63,10 +75,6 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
|
|||||||
if update.chatMember ne null then i.onChatMemberUpdated
|
if update.chatMember ne null then i.onChatMemberUpdated
|
||||||
updateThreadName("chat-join-request")
|
updateThreadName("chat-join-request")
|
||||||
if update.chatJoinRequest ne null then i.onChatJoinRequest
|
if update.chatJoinRequest ne null then i.onChatJoinRequest
|
||||||
|
|
||||||
updateThreadName("#post")
|
|
||||||
i.atEventPost
|
|
||||||
|
|
||||||
} catch case e => {
|
} catch case e => {
|
||||||
val errorMessage = StringBuilder()
|
val errorMessage = StringBuilder()
|
||||||
errorMessage ++= "Event throws unexpected exception:\n"
|
errorMessage ++= "Event throws unexpected exception:\n"
|
||||||
@ -81,8 +89,6 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
|
|||||||
logger error errorMessage.toString
|
logger error errorMessage.toString
|
||||||
coeur.daemons.reporter.exception(e, "on event running")
|
coeur.daemons.reporter.exception(e, "on event running")
|
||||||
}
|
}
|
||||||
|
|
||||||
}}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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.bot.api.{EventEnv, EventListener}
|
||||||
import cc.sukazyo.cono.morny.MornyCoeur
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
import com.pengrad.telegrambot.model.Update
|
|
||||||
|
|
||||||
class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
|
class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
|
||||||
|
|
||||||
private def checkOutdated (timestamp: Int)(using event: EventEnv): Unit =
|
private def checkOutdated (timestamp: Int)(using event: EventEnv): Unit =
|
||||||
if coeur.config.eventIgnoreOutdated && (timestamp < (coeur.coeurStartTimestamp/1000)) then
|
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 onMessage (using event: EventEnv): Unit = checkOutdated(event.update.message.date)
|
||||||
override def onEditedMessage (using event: EventEnv): Unit = checkOutdated(event.update.editedMessage.date)
|
override def onEditedMessage (using event: EventEnv): Unit = checkOutdated(event.update.editedMessage.date)
|
||||||
|
@ -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.data.TelegramStickers
|
||||||
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.TelegramExtensions.Bot.exec
|
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.model.request.ParseMode
|
||||||
import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
|
import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
|
||||||
|
|
||||||
|
@ -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.bot.api.{EventEnv, EventListener}
|
||||||
import cc.sukazyo.cono.morny.MornyCoeur
|
import cc.sukazyo.cono.morny.MornyCoeur
|
||||||
import cc.sukazyo.cono.morny.daemon.{MedicationTimer, MornyDaemons}
|
import com.pengrad.telegrambot.model.Message
|
||||||
import com.pengrad.telegrambot.model.{Message, Update}
|
|
||||||
|
|
||||||
class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
|
class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
|
||||||
|
|
||||||
@ -14,7 +13,7 @@ class OnMedicationNotifyApply (using coeur: MornyCoeur) extends EventListener {
|
|||||||
|
|
||||||
private def editedMessageProcess (edited: Message)(using event: EventEnv): Unit = {
|
private def editedMessageProcess (edited: Message)(using event: EventEnv): Unit = {
|
||||||
if edited.chat.id != coeur.config.medicationNotifyToChat then return;
|
if edited.chat.id != coeur.config.medicationNotifyToChat then return;
|
||||||
coeur.daemons.medicationTimer.refreshNotificationWrite(edited)
|
if coeur.daemons.medicationTimer.refreshNotificationWrite(edited) then
|
||||||
event.setEventOk
|
event.setEventOk
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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.bot.command.MornyCommands
|
||||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||||
import cc.sukazyo.cono.morny.Log.logger
|
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 = {
|
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"
|
logger trace s"got input command {$input} from event-context"
|
||||||
|
|
||||||
for ((name, command_instance) <- commands.commands_uni) {
|
for ((name, command_instance) <- commands.commands_uni) {
|
||||||
|
@ -69,8 +69,8 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
|||||||
else lastNotify_messageId = None
|
else lastNotify_messageId = None
|
||||||
}
|
}
|
||||||
|
|
||||||
def refreshNotificationWrite (edited: Message): Unit = {
|
def refreshNotificationWrite (edited: Message): Boolean = {
|
||||||
if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return
|
if (lastNotify_messageId isEmpty) || (lastNotify_messageId.get != (edited.messageId toInt)) then return false
|
||||||
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
||||||
val editTime = formatDate(edited.editDate*1000, use_timeZone.getTotalSeconds/60/60)
|
val editTime = formatDate(edited.editDate*1000, use_timeZone.getTotalSeconds/60/60)
|
||||||
val entities = ArrayBuffer.empty[MessageEntity]
|
val entities = ArrayBuffer.empty[MessageEntity]
|
||||||
@ -82,6 +82,7 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
|||||||
edited.text + s"\n-- $editTime --"
|
edited.text + s"\n-- $editTime --"
|
||||||
).entities(entities toArray:_*)
|
).entities(entities toArray:_*)
|
||||||
lastNotify_messageId = None
|
lastNotify_messageId = None
|
||||||
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -134,10 +134,13 @@ class MornyReport (using coeur: MornyCoeur) {
|
|||||||
object EventStatistics {
|
object EventStatistics {
|
||||||
|
|
||||||
private var eventTotal = 0
|
private var eventTotal = 0
|
||||||
|
private var eventCanceled = 0
|
||||||
private val runningTime: NumericStatistics[DurationMillis] = NumericStatistics()
|
private val runningTime: NumericStatistics[DurationMillis] = NumericStatistics()
|
||||||
|
|
||||||
def reset (): Unit = {
|
def reset (): Unit = {
|
||||||
eventTotal = 0; runningTime.reset()
|
eventTotal = 0
|
||||||
|
eventCanceled = 0
|
||||||
|
runningTime.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def runningTimeStatisticsHTML: String =
|
private def runningTimeStatisticsHTML: String =
|
||||||
@ -154,11 +157,13 @@ class MornyReport (using coeur: MornyCoeur) {
|
|||||||
def eventStatisticsHTML: String =
|
def eventStatisticsHTML: String =
|
||||||
import cc.sukazyo.cono.morny.util.UseMath.percentageOf as p
|
import cc.sukazyo.cono.morny.util.UseMath.percentageOf as p
|
||||||
val processed = runningTime.count
|
val processed = runningTime.count
|
||||||
val ignored = eventTotal - processed
|
val canceled = eventCanceled
|
||||||
|
val ignored = eventTotal - processed - canceled
|
||||||
// language=html
|
// language=html
|
||||||
s""" - <i>total event received</i>: <code>$eventTotal</code>
|
s""" - <i>total event received</i>: <code>$eventTotal</code>
|
||||||
| - <i>event processed</i>: (<code>${eventTotal p processed}%</code>) <code>$processed</code>
|
|
||||||
| - <i>event ignored</i>: (<code>${eventTotal p ignored}%</code>) <code>$ignored</code>
|
| - <i>event ignored</i>: (<code>${eventTotal p ignored}%</code>) <code>$ignored</code>
|
||||||
|
| - <i>event canceled</i>: (<code>${eventTotal p canceled}%</code>) <code>$canceled</code>
|
||||||
|
| - <i>event processed</i>: (<code>${eventTotal p processed}%</code>) <code>$processed</code>
|
||||||
| - <i>processed time usage</i>:
|
| - <i>processed time usage</i>:
|
||||||
|${runningTimeStatisticsHTML.indent(3)}""".stripMargin
|
|${runningTimeStatisticsHTML.indent(3)}""".stripMargin
|
||||||
|
|
||||||
@ -167,13 +172,23 @@ class MornyReport (using coeur: MornyCoeur) {
|
|||||||
//noinspection ScalaWeakerAccess
|
//noinspection ScalaWeakerAccess
|
||||||
case class EventTimeUsed (it: DurationMillis)
|
case class EventTimeUsed (it: DurationMillis)
|
||||||
override def atEventPost (using event: EventEnv): Unit = {
|
override def atEventPost (using event: EventEnv): Unit = {
|
||||||
|
import event.State
|
||||||
eventTotal += 1
|
eventTotal += 1
|
||||||
if event.isEventOk then {
|
event.state match
|
||||||
|
case State.OK(from) =>
|
||||||
val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup)
|
val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup)
|
||||||
event provide timeUsed
|
event provide timeUsed
|
||||||
logger debug s"event consumed ${timeUsed.it}ms"
|
logger debug
|
||||||
|
s"""event done with OK
|
||||||
|
| with time consumed ${timeUsed.it}ms
|
||||||
|
| by $from""".stripMargin
|
||||||
runningTime ++ timeUsed.it
|
runningTime ++ timeUsed.it
|
||||||
}
|
case State.CANCELED(from) =>
|
||||||
|
eventCanceled += 1
|
||||||
|
logger debug
|
||||||
|
s"""event done with CANCELED"
|
||||||
|
| by $from""".stripMargin
|
||||||
|
case null =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user