better err report for GivenContext, module morny.event_hack

This commit is contained in:
A.C.Sukazyo Eyre 2023-12-23 20:16:16 +08:00
parent 083e5d1b2f
commit 7718ae845a
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
11 changed files with 126 additions and 19 deletions

View File

@ -33,8 +33,7 @@ class MornyCoreModule extends MornyModule {
OnCallMe(), OnCallMe(),
OnCallMsgSend(), OnCallMsgSend(),
OnGetSocial(), OnGetSocial(),
OnMedicationNotifyApply(), OnMedicationNotifyApply()
OnEventHackHandle()
) )
val $MornyHellos = MornyHellos() val $MornyHellos = MornyHellos()
@ -50,7 +49,6 @@ class MornyCoreModule extends MornyModule {
$MornyHellos.Hello, $MornyHellos.Hello,
MornyInfoOnStart(), MornyInfoOnStart(),
GetUsernameAndId(), GetUsernameAndId(),
EventHack(),
Nbnhhsh(), Nbnhhsh(),
$IP186Query.IP, $IP186Query.IP,
$IP186Query.Whois, $IP186Query.Whois,

View File

@ -0,0 +1,18 @@
package cc.sukazyo.cono.morny
import cc.sukazyo.cono.morny.event_hack.ModuleEventHack
import cc.sukazyo.cono.morny.uni_meow.ModuleUniMeow
object ServerModulesLoader {
def load (): List[MornyModule] = {
List(
ModuleEventHack(),
ModuleUniMeow(),
MornyCoreModule()
)
}
}

View File

@ -2,12 +2,12 @@ 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
import cc.sukazyo.cono.morny.event_hack.EventHacker
class MornyDaemons (using val coeur: MornyCoeur) { class MornyDaemons (using val coeur: MornyCoeur) {
val medicationTimer: MedicationTimer = MedicationTimer() val medicationTimer: MedicationTimer = MedicationTimer()
val reporter: MornyReport = MornyReport() val reporter: MornyReport = MornyReport()
val eventHack: EventHacker = EventHacker()
def start (): Unit = { def start (): Unit = {

View File

@ -1,5 +1,7 @@
package cc.sukazyo.cono.morny.bot.command package cc.sukazyo.cono.morny.event_hack
import cc.sukazyo.cono.morny.MornyCoeur import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.bot.command.{ICommandAlias, ITelegramCommand}
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.tgapi.TelegramExtensions.Bot.exec import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
@ -8,7 +10,7 @@ import com.pengrad.telegrambot.request.SendSticker
import scala.language.postfixOps import scala.language.postfixOps
class EventHack (using coeur: MornyCoeur) extends ITelegramCommand { class BotCmdEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) extends ITelegramCommand {
override val name: String = "event_hack" override val name: String = "event_hack"
override val aliases: List[ICommandAlias] = Nil override val aliases: List[ICommandAlias] = Nil
@ -17,7 +19,7 @@ class EventHack (using coeur: MornyCoeur) 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} import hacker.{registerHack, HackType}
val x_mode = if (command.args nonEmpty) command.args(0) else "" val x_mode = if (command.args nonEmpty) command.args(0) else ""

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.bot.event package cc.sukazyo.cono.morny.event_hack
import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener} import cc.sukazyo.cono.morny.bot.api.{EventEnv, EventListener}
import cc.sukazyo.cono.morny.Log.logger import cc.sukazyo.cono.morny.Log.logger
@ -11,11 +11,11 @@ import com.pengrad.telegrambot.request.SendMessage
import scala.collection.mutable import scala.collection.mutable
import scala.language.postfixOps import scala.language.postfixOps
class OnEventHackHandle (using coeur: MornyCoeur) extends EventListener { class BotEventEventHackHandle (using hacker: EventHacker)(using coeur: MornyCoeur) extends EventListener {
private def trigger (chat_id: Long, from_id: Long)(using event: EventEnv): Unit = private def trigger (chat_id: Long, from_id: Long)(using event: EventEnv): Unit =
given Update = event.update given Update = event.update
if coeur.daemons.eventHack.trigger(chat_id, from_id) then if hacker.trigger(chat_id, from_id) then
event.setEventOk event.setEventOk
override def onMessage (using event: EventEnv): Unit = override def onMessage (using event: EventEnv): Unit =

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.daemon package cc.sukazyo.cono.morny.event_hack
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

View File

@ -0,0 +1,35 @@
package cc.sukazyo.cono.morny.event_hack
import cc.sukazyo.cono.morny.internal.MornyInternalModule
import cc.sukazyo.cono.morny.MornyCoeur
class ModuleEventHack extends MornyInternalModule {
override val id: String = "morny.event_hack"
override val name: String = "Morny Event Hack"
override val description: String | Null =
// language=markdown
"""The `/event_hack` command which can make morny output the next
|serialized event.
|""".stripMargin
override def onInitializingPre (using MornyCoeur)(cxt: MornyCoeur.OnInitializingPreContext): Unit = {
import cxt.*
given hacker: EventHacker = EventHacker()
externalContext << hacker
givenCxt << hacker
}
override def onInitializing (using MornyCoeur)(cxt: MornyCoeur.OnInitializingContext): Unit = {
import cxt.*
given EventHacker = externalContext >!> classOf[EventHacker]
commandManager.register(BotCmdEventHack())
eventManager.register(BotEventEventHackHandle())
}
}

View File

@ -0,0 +1,9 @@
package cc.sukazyo.cono.morny.internal
import cc.sukazyo.cono.morny.{MornyModule, MornySystem}
trait MornyInternalModule extends MornyModule {
override val version: String = MornySystem.VERSION
}

View File

@ -1,12 +1,12 @@
package cc.sukazyo.cono.morny.uni_meow package cc.sukazyo.cono.morny.uni_meow
import cc.sukazyo.cono.morny.{MornyCoeur, MornyModule, MornySystem} import cc.sukazyo.cono.morny.MornyCoeur
import cc.sukazyo.cono.morny.internal.MornyInternalModule
class ModuleUniMeow extends MornyModule { class ModuleUniMeow extends MornyInternalModule {
override val id: String = "coeur.uni_meow" override val id: String = "coeur.uni_meow"
override val name: String = "Coeur Uni-Meow Commands" override val name: String = "Coeur Uni-Meow Commands"
override val version: String = MornySystem.VERSION
override val description: String | Null = override val description: String | Null =
// language=Markdown // language=Markdown
"""Provides support for unicode command in Telegram. Also provided some """Provides support for unicode command in Telegram. Also provided some

View File

@ -1,13 +1,33 @@
package cc.sukazyo.cono.morny.util package cc.sukazyo.cono.morny.util
import cc.sukazyo.cono.morny.util.GivenContext.ContextNotGivenException import cc.sukazyo.cono.morny.util.GivenContext.{ContextNotGivenException, FolderClass, RequestItemClass}
import cc.sukazyo.messiva.utils.StackUtils
import scala.annotation.targetName import scala.annotation.targetName
import scala.collection.mutable import scala.collection.mutable
import scala.reflect.{classTag, ClassTag} import scala.reflect.{classTag, ClassTag}
import scala.util.boundary
object GivenContext { object GivenContext {
class ContextNotGivenException extends NoSuchElementException case class FolderClass (clazz: Option[Class[?]])
object FolderClass:
def default: FolderClass = FolderClass(None)
case class RequestItemClass (clazz: Class[?])
private def lastNonGCStack: StackTraceElement =
boundary {
for (stack <- StackUtils.getStackTrace(0)) {
if (!stack.getClassName.startsWith(classOf[GivenContext].getName))
boundary break stack
}
StackTraceElement("unknown", "unknown", "unknown", -1)
}
class ContextNotGivenException (using
val requestItemClass: RequestItemClass,
val folderClass: FolderClass = FolderClass.default,
val requestStack: StackTraceElement = UseStacks.getStackHeadBeforeClass[GivenContext]
) extends NoSuchElementException (
s"None of the ${requestItemClass.clazz.getSimpleName} is in the context${folderClass.clazz.map(" and owned by " + _.getSimpleName).getOrElse("")}, which is required by $requestStack."
)
} }
/** A mutable collection that can store(provide) any typed value and read(use/consume) that value by type. /** A mutable collection that can store(provide) any typed value and read(use/consume) that value by type.
@ -67,7 +87,8 @@ class GivenContext {
private type CxtOption[T] = Either[ContextNotGivenException, T] private type CxtOption[T] = Either[ContextNotGivenException, T]
def use [T: ClassTag]: CxtOption[T] = def use [T: ClassTag]: CxtOption[T] =
variables get classTag[T].runtimeClass match given t: RequestItemClass = RequestItemClass(classTag[T].runtimeClass)
variables get t.clazz match
case Some(i) => Right(i.asInstanceOf[T]) case Some(i) => Right(i.asInstanceOf[T])
case None => Left(ContextNotGivenException()) case None => Left(ContextNotGivenException())
def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
@ -101,9 +122,13 @@ class GivenContext {
this.provide[T](i) this.provide[T](i)
def use [T: ClassTag]: CxtOption[T] = def use [T: ClassTag]: CxtOption[T] =
variablesWithOwner(classTag[O].runtimeClass) get classTag[T].runtimeClass match given t: RequestItemClass = RequestItemClass(classTag[T].runtimeClass)
given u: FolderClass = FolderClass(Some(classTag[O].runtimeClass))
variablesWithOwner get u.clazz.get match
case Some(varColl) => varColl get t.clazz match
case Some(i) => Right(i.asInstanceOf[T]) case Some(i) => Right(i.asInstanceOf[T])
case None => Left(ContextNotGivenException()) case None => Left(ContextNotGivenException())
case None => Left(ContextNotGivenException())
def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] = def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
use[T] match use[T] match
case Left(_) => ConsumeFailed[U]() case Left(_) => ConsumeFailed[U]()

View File

@ -0,0 +1,20 @@
package cc.sukazyo.cono.morny.util
import cc.sukazyo.messiva.utils.StackUtils
import scala.reflect.{classTag, ClassTag}
import scala.util.boundary
object UseStacks {
def getStackHeadBeforeClass[T: ClassTag]: StackTraceElement = {
boundary {
for (stack <- StackUtils.getStackTrace(1)) {
if (!stack.getClassName.startsWith(classTag[T].runtimeClass.getName))
boundary break stack
}
StackTraceElement("unknown", "unknown", "unknown", -1)
}
}
}