mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-22 19:24:53 +08:00
Compare commits
2 Commits
025f152417
...
d180e7d04f
Author | SHA1 | Date | |
---|---|---|---|
d180e7d04f | |||
4908110c80 |
@ -8,7 +8,7 @@ object MornyConfiguration {
|
|||||||
val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono"
|
val MORNY_CODE_STORE = "https://github.com/Eyre-S/Coeur-Morny-Cono"
|
||||||
val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s"
|
val MORNY_COMMIT_PATH = "https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s"
|
||||||
|
|
||||||
val VERSION = "2.0.0-alpha16"
|
val VERSION = "2.0.0-alpha17"
|
||||||
val VERSION_DELTA: Option[String] = None
|
val VERSION_DELTA: Option[String] = None
|
||||||
val CODENAME = "guanggu"
|
val CODENAME = "guanggu"
|
||||||
|
|
||||||
@ -16,28 +16,28 @@ object MornyConfiguration {
|
|||||||
|
|
||||||
val dependencies: Seq[ModuleID] = Seq(
|
val dependencies: Seq[ModuleID] = Seq(
|
||||||
|
|
||||||
"com.github.spotbugs" % "spotbugs-annotations" % "4.7.3" % Compile,
|
"com.github.spotbugs" % "spotbugs-annotations" % "4.8.2" % Compile,
|
||||||
|
|
||||||
"cc.sukazyo" % "messiva" % "0.2.0",
|
"cc.sukazyo" % "messiva" % "0.2.0",
|
||||||
"cc.sukazyo" % "resource-tools" % "0.2.2",
|
"cc.sukazyo" % "resource-tools" % "0.2.2",
|
||||||
|
|
||||||
"com.github.pengrad" % "java-telegram-bot-api" % "6.2.0",
|
"com.github.pengrad" % "java-telegram-bot-api" % "6.2.0",
|
||||||
"org.http4s" %% "http4s-dsl" % "0.23.24",
|
"org.http4s" %% "http4s-dsl" % "0.23.25",
|
||||||
"org.http4s" %% "http4s-circe" % "0.23.24",
|
"org.http4s" %% "http4s-circe" % "0.23.25",
|
||||||
"org.http4s" %% "http4s-netty-server" % "0.5.11",
|
"org.http4s" %% "http4s-netty-server" % "0.5.12",
|
||||||
|
|
||||||
"com.softwaremill.sttp.client3" %% "core" % "3.9.0",
|
"com.softwaremill.sttp.client3" %% "core" % "3.9.2",
|
||||||
"com.softwaremill.sttp.client3" %% "okhttp-backend" % "3.9.0",
|
"com.softwaremill.sttp.client3" %% "okhttp-backend" % "3.9.2",
|
||||||
"com.squareup.okhttp3" % "okhttp" % "4.11.0" % Runtime,
|
"com.squareup.okhttp3" % "okhttp" % "4.12.0" % Runtime,
|
||||||
|
|
||||||
"org.typelevel" %% "case-insensitive" % "1.4.0",
|
"org.typelevel" %% "case-insensitive" % "1.4.0",
|
||||||
"com.google.code.gson" % "gson" % "2.10.1",
|
"com.google.code.gson" % "gson" % "2.10.1",
|
||||||
"io.circe" %% "circe-core" % "0.14.6",
|
"io.circe" %% "circe-core" % "0.14.6",
|
||||||
"io.circe" %% "circe-generic" % "0.14.6",
|
"io.circe" %% "circe-generic" % "0.14.6",
|
||||||
"io.circe" %% "circe-parser" % "0.14.6",
|
"io.circe" %% "circe-parser" % "0.14.6",
|
||||||
"org.jsoup" % "jsoup" % "1.16.2",
|
"org.jsoup" % "jsoup" % "1.17.2",
|
||||||
|
|
||||||
"com.cronutils" % "cron-utils" % "9.2.0",
|
"com.cronutils" % "cron-utils" % "9.2.1",
|
||||||
|
|
||||||
// used for disable slf4j
|
// used for disable slf4j
|
||||||
// due to the slf4j api have been used in the following libraries:
|
// due to the slf4j api have been used in the following libraries:
|
||||||
@ -47,8 +47,8 @@ object MornyConfiguration {
|
|||||||
"org.scalatest" %% "scalatest" % "3.2.17" % Test,
|
"org.scalatest" %% "scalatest" % "3.2.17" % Test,
|
||||||
"org.scalatest" %% "scalatest-freespec" % "3.2.17" % Test,
|
"org.scalatest" %% "scalatest-freespec" % "3.2.17" % Test,
|
||||||
// for test report
|
// for test report
|
||||||
"com.vladsch.flexmark" % "flexmark" % "0.64.0" % Test,
|
"com.vladsch.flexmark" % "flexmark" % "0.64.8" % Test,
|
||||||
"com.vladsch.flexmark" % "flexmark-profile-pegdown" % "0.64.0" % Test
|
"com.vladsch.flexmark" % "flexmark-profile-pegdown" % "0.64.8" % Test
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import cc.sukazyo.cono.morny.core.MornyCoeur.*
|
|||||||
import cc.sukazyo.cono.morny.core.bot.api.{EventListenerManager, MornyCommandManager, MornyQueryManager}
|
import cc.sukazyo.cono.morny.core.bot.api.{EventListenerManager, MornyCommandManager, MornyQueryManager}
|
||||||
import cc.sukazyo.cono.morny.core.bot.api.messages.ThreadingManager
|
import cc.sukazyo.cono.morny.core.bot.api.messages.ThreadingManager
|
||||||
import cc.sukazyo.cono.morny.core.bot.event.{MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock}
|
import cc.sukazyo.cono.morny.core.bot.event.{MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock}
|
||||||
import cc.sukazyo.cono.morny.core.bot.internal.ThreadingManagerImpl
|
import cc.sukazyo.cono.morny.core.bot.internal.{ErrorMessageManager, ThreadingManagerImpl}
|
||||||
import cc.sukazyo.cono.morny.core.http.api.{HttpServer, MornyHttpServerContext}
|
import cc.sukazyo.cono.morny.core.http.api.{HttpServer, MornyHttpServerContext}
|
||||||
import cc.sukazyo.cono.morny.core.http.internal.MornyHttpServerContextImpl
|
import cc.sukazyo.cono.morny.core.http.internal.MornyHttpServerContextImpl
|
||||||
import cc.sukazyo.cono.morny.reporter.MornyReport
|
import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||||
@ -171,6 +171,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
|||||||
val trusted: MornyTrusted = MornyTrusted()
|
val trusted: MornyTrusted = MornyTrusted()
|
||||||
private val _messageThreading: ThreadingManagerImpl = ThreadingManagerImpl(using account)
|
private val _messageThreading: ThreadingManagerImpl = ThreadingManagerImpl(using account)
|
||||||
val messageThreading: ThreadingManager = _messageThreading
|
val messageThreading: ThreadingManager = _messageThreading
|
||||||
|
val errorMessageManager: ErrorMessageManager = ErrorMessageManager()
|
||||||
|
|
||||||
val eventManager: EventListenerManager = EventListenerManager()
|
val eventManager: EventListenerManager = EventListenerManager()
|
||||||
val commands: MornyCommandManager = MornyCommandManager()
|
val commands: MornyCommandManager = MornyCommandManager()
|
||||||
@ -211,6 +212,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
|||||||
|
|
||||||
DirectMsgClear(),
|
DirectMsgClear(),
|
||||||
_messageThreading.CancelCommand,
|
_messageThreading.CancelCommand,
|
||||||
|
errorMessageManager.ShowErrorMessageCommand,
|
||||||
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,24 @@
|
|||||||
|
package cc.sukazyo.cono.morny.core.bot.api
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||||
|
import cc.sukazyo.cono.morny.core.bot.api.messages.ErrorMessage
|
||||||
|
|
||||||
|
/** Bot extensions for Morny feature.
|
||||||
|
*/
|
||||||
|
object BotExtension {
|
||||||
|
|
||||||
|
extension (errorMessage: ErrorMessage[?, ?]) {
|
||||||
|
|
||||||
|
/** Submit this [[ErrorMessage]] to a [[MornyCoeur]].
|
||||||
|
*
|
||||||
|
* Will send this [[ErrorMessage]] with the basic send config.
|
||||||
|
*
|
||||||
|
* @see [[cc.sukazyo.cono.morny.core.bot.internal.ErrorMessageManager.sendErrorMessage]]
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
def submit (using coeur: MornyCoeur): Unit =
|
||||||
|
coeur.errorMessageManager.sendErrorMessage(errorMessage)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,38 +7,77 @@ import com.pengrad.telegrambot.model.Update
|
|||||||
|
|
||||||
import scala.collection.mutable
|
import scala.collection.mutable
|
||||||
|
|
||||||
|
/** A Telegram event context that holds status of the event.
|
||||||
|
*
|
||||||
|
* @param update Associated raw [[Update]] object.
|
||||||
|
*/
|
||||||
class EventEnv (
|
class EventEnv (
|
||||||
|
|
||||||
val update: Update
|
val update: Update
|
||||||
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
/** This status contains a [[StackTraceElement]] that shows where the status is set. */
|
||||||
trait StateSource (val from: StackTraceElement)
|
trait StateSource (val from: StackTraceElement)
|
||||||
|
/** Available status of the event. */
|
||||||
enum State:
|
enum State:
|
||||||
|
/** The event is successfully processed by someone event listener. */
|
||||||
case OK (_from: StackTraceElement) extends State with StateSource(_from)
|
case OK (_from: StackTraceElement) extends State with StateSource(_from)
|
||||||
|
/** The event is canceled for some reason, it is recommended be ignored. */
|
||||||
case CANCELED (_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 _status: mutable.ListBuffer[State] = mutable.ListBuffer.empty
|
||||||
|
/** [[GivenContext Given Contexts]] associated to the event. Can be used to store and share
|
||||||
|
* data between event listeners.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
val givenCxt: GivenContext = GivenContext()
|
val givenCxt: GivenContext = GivenContext()
|
||||||
|
/** The [[EpochMillis]] time that bot received this event and preparing to process it.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
val timeStartup: EpochMillis = System.currentTimeMillis
|
val timeStartup: EpochMillis = System.currentTimeMillis
|
||||||
|
|
||||||
|
/** If this event is processed.
|
||||||
|
*
|
||||||
|
* Not only [[State.OK]] but also [[State.CANCELED]] will been seen as processed.
|
||||||
|
*
|
||||||
|
* @since 1.2.0
|
||||||
|
*
|
||||||
|
* @return `true` if the event have been processed, `false` otherwise.
|
||||||
|
*/
|
||||||
def isEventOk: Boolean = _status.lastOption match
|
def isEventOk: Boolean = _status.lastOption match
|
||||||
case Some(x) if x == State.OK => true
|
case Some(x) if x == State.OK => true
|
||||||
case _ => false
|
case _ => false
|
||||||
|
|
||||||
|
/** Set the event status to [[State.OK]].
|
||||||
|
*
|
||||||
|
* This will push a new [[State.OK]] to the status list.
|
||||||
|
*/
|
||||||
//noinspection UnitMethodIsParameterless
|
//noinspection UnitMethodIsParameterless
|
||||||
def setEventOk: Unit =
|
def setEventOk: Unit =
|
||||||
_status += State.OK(StackUtils.getStackTrace(1).head)
|
_status += State.OK(StackUtils.getStackTrace(1).head)
|
||||||
|
|
||||||
|
/** Set the event status to [[State.CANCELED]].
|
||||||
|
*
|
||||||
|
* This will push a new [[State.CANCELED]] to the status list.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
//noinspection UnitMethodIsParameterless
|
//noinspection UnitMethodIsParameterless
|
||||||
def setEventCanceled: Unit =
|
def setEventCanceled: Unit =
|
||||||
_status += State.CANCELED(StackUtils.getStackTrace(1).head)
|
_status += State.CANCELED(StackUtils.getStackTrace(1).head)
|
||||||
|
|
||||||
|
/** Get the last [[State]] set of the event.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
def state: State|Null =
|
def state: State|Null =
|
||||||
_status.lastOption match
|
_status.lastOption match
|
||||||
case Some(x) => x
|
case Some(x) => x
|
||||||
case None => null
|
case None => null
|
||||||
|
|
||||||
|
/** Get all the status set of the event. The earlier status set is in the left.
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
def status: List[State] =
|
def status: List[State] =
|
||||||
_status.toList
|
_status.toList
|
||||||
|
|
||||||
|
@ -0,0 +1,74 @@
|
|||||||
|
package cc.sukazyo.cono.morny.core.bot.api.messages
|
||||||
|
|
||||||
|
import com.pengrad.telegrambot.request.AbstractSendRequest
|
||||||
|
|
||||||
|
/** A error message based on Telegram's [[AbstractSendRequest]].
|
||||||
|
*
|
||||||
|
* Contains two types ([[simple]] and [[complex]]) message that
|
||||||
|
* allows to choose one to show in different cases.
|
||||||
|
*
|
||||||
|
* Also there contains a [[context]] (typed with [[MessagingContext.WithMessage]])
|
||||||
|
* that infers the source of this error message, also can be used for
|
||||||
|
* the unique key of one error message.
|
||||||
|
*
|
||||||
|
* There's also a [[ErrorMessage.Types]] enum infers to the two type. You
|
||||||
|
* can use [[getByType]] (or [[getByTypeNormal]]) method to get the specific
|
||||||
|
* type's message of that type.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*
|
||||||
|
* @see [[cc.sukazyo.cono.morny.core.bot.internal.ErrorMessageManager]] This
|
||||||
|
* ErrorMessage's manager (consumer) implementation by the Morny Coeur.
|
||||||
|
* @see [[cc.sukazyo.cono.morny.core.bot.api.BotExtension.submit]] Simple
|
||||||
|
* way to consume this.
|
||||||
|
*
|
||||||
|
* @tparam T1 Type of the simple error message
|
||||||
|
* @tparam T2 Type of the complex error message
|
||||||
|
*/
|
||||||
|
trait ErrorMessage[T1 <: AbstractSendRequest[T1], T2 <: AbstractSendRequest[T2]] {
|
||||||
|
|
||||||
|
val simple: T1
|
||||||
|
val complex: T2
|
||||||
|
|
||||||
|
val context: MessagingContext.WithMessage
|
||||||
|
|
||||||
|
/** Get the simple or complex message by the given [[ErrorMessage.Types]] infer.
|
||||||
|
*
|
||||||
|
* This method returns a union type [[T1]] and [[T2]]. This may be
|
||||||
|
* not useful when you don't care about the specific return types (maybe for
|
||||||
|
* the most times). You can use [[getByTypeNormal]] instead.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
def getByType (t: ErrorMessage.Types): AbstractSendRequest[T1] | AbstractSendRequest[T2] =
|
||||||
|
t match
|
||||||
|
case ErrorMessage.Types.Simple => simple
|
||||||
|
case ErrorMessage.Types.Complex => complex
|
||||||
|
|
||||||
|
/** Get the simple or complex message by the given [[ErrorMessage.Types]] infer.
|
||||||
|
*
|
||||||
|
* This works exactly the same with the [[getByType]], the only difference is
|
||||||
|
* this returns with a bit universal type `AbstractSendRequest[?]`.
|
||||||
|
*
|
||||||
|
* @see [[getByType]]
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
def getByTypeNormal (t: ErrorMessage.Types): AbstractSendRequest[?] =
|
||||||
|
getByType(t)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
object ErrorMessage {
|
||||||
|
|
||||||
|
enum Types:
|
||||||
|
case Simple
|
||||||
|
case Complex
|
||||||
|
|
||||||
|
def apply [T1 <: AbstractSendRequest[T1], T2<: AbstractSendRequest[T2]]
|
||||||
|
(_simple: T1, _complex: T2)(using cxt: MessagingContext.WithMessage): ErrorMessage[T1, T2] =
|
||||||
|
new ErrorMessage[T1, T2]:
|
||||||
|
override val simple: T1 = _simple
|
||||||
|
override val complex: T2 = _complex
|
||||||
|
override val context: MessagingContext.WithMessage = cxt
|
||||||
|
|
||||||
|
}
|
@ -1,5 +1,6 @@
|
|||||||
package cc.sukazyo.cono.morny.core.bot.api.messages
|
package cc.sukazyo.cono.morny.core.bot.api.messages
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.Standardize.*
|
||||||
import com.pengrad.telegrambot.model.{Chat, Message, User}
|
import com.pengrad.telegrambot.model.{Chat, Message, User}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -7,37 +8,69 @@ import com.pengrad.telegrambot.model.{Chat, Message, User}
|
|||||||
*/
|
*/
|
||||||
trait MessagingContext:
|
trait MessagingContext:
|
||||||
val bind_chat: Chat
|
val bind_chat: Chat
|
||||||
|
def toChatKey: MessagingContext.Key =
|
||||||
|
MessagingContext.Key(this)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @since 2.0.0
|
* @since 2.0.0
|
||||||
*/
|
*/
|
||||||
object MessagingContext {
|
object MessagingContext {
|
||||||
|
|
||||||
given String = "aaa"
|
case class Key (chatId: ChatID)
|
||||||
|
object Key:
|
||||||
|
def apply (it: MessagingContext): Key = Key(it.bind_chat.id)
|
||||||
def apply (_chat: Chat): MessagingContext =
|
def apply (_chat: Chat): MessagingContext =
|
||||||
new MessagingContext:
|
new MessagingContext:
|
||||||
override val bind_chat: Chat = _chat
|
override val bind_chat: Chat = _chat
|
||||||
|
|
||||||
trait WithUser extends MessagingContext:
|
trait WithUser extends MessagingContext:
|
||||||
val bind_user: User
|
val bind_user: User
|
||||||
|
def toChatUserKey: WithUser.Key =
|
||||||
|
WithUser.Key(this)
|
||||||
|
object WithUser:
|
||||||
|
case class Key (chatID: ChatID, userId: UserID)
|
||||||
|
object Key:
|
||||||
|
def apply (it: WithUser): Key = Key(it.bind_chat.id, it.bind_user.id)
|
||||||
|
def apply (_chat: Chat, _user: User): WithUser =
|
||||||
|
new WithUser:
|
||||||
|
override val bind_chat: Chat = _chat
|
||||||
|
override val bind_user: User = _user
|
||||||
def apply (_chat: Chat, _user: User): WithUser =
|
def apply (_chat: Chat, _user: User): WithUser =
|
||||||
new WithUser:
|
WithUser(_chat, _user)
|
||||||
override val bind_chat: Chat = _chat
|
|
||||||
override val bind_user: User = _user
|
|
||||||
trait WithMessage extends MessagingContext:
|
trait WithMessage extends MessagingContext:
|
||||||
val bind_message: Message
|
val bind_message: Message
|
||||||
|
def toChatMessageKey: WithMessage.Key =
|
||||||
|
WithMessage.Key(this)
|
||||||
|
object WithMessage:
|
||||||
|
case class Key (chatId: ChatID, messageId: MessageID)
|
||||||
|
object Key:
|
||||||
|
def apply (it: WithMessage): Key = Key(it.bind_chat.id, it.bind_message.messageId)
|
||||||
|
def apply (_chat: Chat, _message: Message): WithMessage =
|
||||||
|
new WithMessage:
|
||||||
|
override val bind_chat: Chat = _chat
|
||||||
|
override val bind_message: Message = _message
|
||||||
def apply (_chat: Chat, _message: Message): WithMessage =
|
def apply (_chat: Chat, _message: Message): WithMessage =
|
||||||
new WithMessage:
|
WithMessage(_chat, _message)
|
||||||
override val bind_chat: Chat = _chat
|
|
||||||
override val bind_message: Message = _message
|
trait WithUserAndMessage extends MessagingContext with WithMessage with WithUser:
|
||||||
trait WithUserAndMessage extends MessagingContext with WithMessage with WithUser
|
def toChatUserMessageKey: WithUserAndMessage.Key =
|
||||||
|
WithUserAndMessage.Key(this)
|
||||||
|
object WithUserAndMessage:
|
||||||
|
case class Key (chatId: ChatID, userId: UserID, messageId: MessageID)
|
||||||
|
object Key:
|
||||||
|
def apply (it: WithUserAndMessage): Key = Key(it.bind_chat.id, it.bind_user.id, it.bind_message.messageId)
|
||||||
|
def apply (_chat: Chat, _user: User, _message: Message): WithUserAndMessage =
|
||||||
|
new WithUserAndMessage:
|
||||||
|
override val bind_chat: Chat = _chat
|
||||||
|
override val bind_user: User = _user
|
||||||
|
override val bind_message: Message = _message
|
||||||
def apply (_chat: Chat, _user: User, _message: Message): WithUserAndMessage =
|
def apply (_chat: Chat, _user: User, _message: Message): WithUserAndMessage =
|
||||||
new WithUserAndMessage:
|
WithUserAndMessage(_chat, _user, _message)
|
||||||
override val bind_chat: Chat = _chat
|
|
||||||
override val bind_user: User = _user
|
|
||||||
override val bind_message: Message = _message
|
|
||||||
|
|
||||||
/** Extract a message context from a message (or message event).
|
/** Extract a message context from a message (or message event).
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
*
|
*
|
||||||
* @param message The message.
|
* @param message The message.
|
||||||
* @return The message context, contains the message's belongs chat, sender user and message itself.
|
* @return The message context, contains the message's belongs chat, sender user and message itself.
|
||||||
|
@ -2,6 +2,7 @@ package cc.sukazyo.cono.morny.core.bot.command
|
|||||||
|
|
||||||
import cc.sukazyo.cono.morny.core.{MornyCoeur, MornySystem}
|
import cc.sukazyo.cono.morny.core.{MornyCoeur, MornySystem}
|
||||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||||
|
import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext}
|
||||||
import cc.sukazyo.cono.morny.data.MornyInformation.*
|
import cc.sukazyo.cono.morny.data.MornyInformation.*
|
||||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||||
import cc.sukazyo.cono.morny.reporter.MornyReport
|
import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||||
@ -36,7 +37,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
|||||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||||
|
|
||||||
if (command.args isEmpty) {
|
if (command.args isEmpty) {
|
||||||
echoInfo(event.message.chat.id, event.message.messageId)
|
echoInfo(event)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -53,9 +54,24 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def echoInfo (chatId: Long, replyTo: Int): Unit = {
|
private def echoInfo (update: Update): Unit = {
|
||||||
|
val cxt = MessagingContext.extract(using update.message)
|
||||||
|
val cxtReplied = Option(update.message.replyToMessage).map(MessagingContext.extract(using _))
|
||||||
|
|
||||||
|
cxtReplied match
|
||||||
|
case None =>
|
||||||
|
case Some(_cxtReplied) =>
|
||||||
|
// check if theres associated information about error message on the replied context.
|
||||||
|
// if there really is, that means the replied message is a error message,
|
||||||
|
// so the error message's complex information will be sent
|
||||||
|
coeur.errorMessageManager.inspectMessage(_cxtReplied.toChatMessageKey) match
|
||||||
|
case None =>
|
||||||
|
case Some(errMessage) =>
|
||||||
|
coeur.errorMessageManager.sendErrorMessage(errMessage, ErrorMessage.Types.Complex, Some(cxt))
|
||||||
|
|
||||||
|
// if theres no any associated information on the context
|
||||||
SendPhoto(
|
SendPhoto(
|
||||||
chatId,
|
cxt.bind_chat.id,
|
||||||
getAboutPic
|
getAboutPic
|
||||||
).caption(
|
).caption(
|
||||||
s"""<b>Morny Cono</b>
|
s"""<b>Morny Cono</b>
|
||||||
@ -63,8 +79,9 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
|||||||
|————————————————
|
|————————————————
|
||||||
|$getMornyAboutLinksHTML"""
|
|$getMornyAboutLinksHTML"""
|
||||||
.stripMargin
|
.stripMargin
|
||||||
).parseMode(ParseMode HTML).replyToMessageId(replyTo)
|
).parseMode(ParseMode HTML).replyToMessageId(cxt.bind_message.messageId)
|
||||||
.unsafeExecute
|
.unsafeExecute
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private def echoStickers (using command: InputCommand, event: Update): Unit = {
|
private def echoStickers (using command: InputCommand, event: Update): Unit = {
|
||||||
|
@ -0,0 +1,110 @@
|
|||||||
|
package cc.sukazyo.cono.morny.core.bot.internal
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||||
|
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||||
|
import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext}
|
||||||
|
import cc.sukazyo.cono.morny.util.schedule.{DelayedTask, Scheduler}
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||||
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||||
|
import cc.sukazyo.cono.morny.util.EpochDateTime.DurationMillis
|
||||||
|
import com.pengrad.telegrambot.TelegramBot
|
||||||
|
import com.pengrad.telegrambot.model.Update
|
||||||
|
import com.pengrad.telegrambot.request.{AbstractSendRequest, SendMessage}
|
||||||
|
|
||||||
|
class ErrorMessageManager (using coeur: MornyCoeur) {
|
||||||
|
given TelegramBot = coeur.account
|
||||||
|
|
||||||
|
private val expiresDuration: DurationMillis = 1000 * 60 * 60 * 5 // 5 hours
|
||||||
|
|
||||||
|
private val errorMessageMap = collection.mutable.Map[MessagingContext.WithMessage.Key, ErrorMessage[?, ?]]()
|
||||||
|
private val errorMessageMapCleaner = Scheduler(isDaemon = true)
|
||||||
|
private def putErrorMessage (key: MessagingContext.WithMessage.Key, message: ErrorMessage[?, ?]): Unit =
|
||||||
|
errorMessageMap.synchronized: // remove an error message after the given expires duration.
|
||||||
|
errorMessageMap += (key -> message)
|
||||||
|
errorMessageMapCleaner ++ DelayedTask("", expiresDuration, {
|
||||||
|
errorMessageMap.remove(key)
|
||||||
|
})
|
||||||
|
|
||||||
|
/** Send an [[ErrorMessage]] and add this error message to ErrorMessage stash.
|
||||||
|
*
|
||||||
|
* This will execute the SendRequest using the default send config.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
def sendErrorMessage (message: ErrorMessage[?, ?]): Unit =
|
||||||
|
// todo: which should be sent using user config
|
||||||
|
val useType = ErrorMessage.Types.Simple
|
||||||
|
sendErrorMessage(message, useType, isNewMessage = true)
|
||||||
|
|
||||||
|
/** Send an [[ErrorMessage]] and add it to the stash if it is new.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*
|
||||||
|
* @param message The [[ErrorMessage]] to be sent.
|
||||||
|
* @param useType Which type of the message should be sent.
|
||||||
|
* @param injectedSendContext A context that determine where and how the message should
|
||||||
|
* be sent. Currently only the message id in this context will
|
||||||
|
* be used to determine the reply to message.
|
||||||
|
*
|
||||||
|
* This is part of the API limitation, but due to in normal cases,
|
||||||
|
* the source error message must be in the same chat so it may
|
||||||
|
* make sense.
|
||||||
|
* @param isNewMessage Is this ErrorMessage is a new error message, otherwise it should be
|
||||||
|
* an existed error message that just calling this method because of
|
||||||
|
* need to be resent some messages.
|
||||||
|
*
|
||||||
|
* If this is new, the message will be added to the stash, if not, it
|
||||||
|
* should be comes from the stash and will not be added to the stash again.
|
||||||
|
*
|
||||||
|
* Default is false.
|
||||||
|
*/
|
||||||
|
def sendErrorMessage (
|
||||||
|
message: ErrorMessage[?, ?],
|
||||||
|
useType: ErrorMessage.Types,
|
||||||
|
injectedSendContext: Option[MessagingContext.WithMessage] = None,
|
||||||
|
isNewMessage: Boolean = false
|
||||||
|
): Unit = {
|
||||||
|
val sendMessage = message
|
||||||
|
.getByTypeNormal(useType)
|
||||||
|
.asInstanceOf[AbstractSendRequest[Nothing]]
|
||||||
|
injectedSendContext match
|
||||||
|
case None =>
|
||||||
|
case Some(cxt) =>
|
||||||
|
sendMessage.replyToMessageId(cxt.bind_message.messageId)
|
||||||
|
val response = sendMessage.unsafeExecute
|
||||||
|
if isNewMessage then
|
||||||
|
val key = MessagingContext.extract(using response.message).toChatMessageKey
|
||||||
|
putErrorMessage(key, message)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get the stashed [[ErrorMessage]] associated with the [[MessagingContext.WithMessage.Key message key]].
|
||||||
|
*
|
||||||
|
* If the message key does not associated with any error message in the stash, then
|
||||||
|
* [[None]] will be returned.
|
||||||
|
*
|
||||||
|
* Notice that one [[ErrorMessage]] will only be stashed for 5 hours then it will be
|
||||||
|
* cleaned up from the stash.
|
||||||
|
*
|
||||||
|
* @since 2.0.0
|
||||||
|
*/
|
||||||
|
def inspectMessage (messageKey: MessagingContext.WithMessage.Key): Option[ErrorMessage[?, ?]] =
|
||||||
|
errorMessageMap.get(messageKey)
|
||||||
|
|
||||||
|
object ShowErrorMessageCommand extends ISimpleCommand {
|
||||||
|
override val name: String = "inspect"
|
||||||
|
override val aliases: List[ICommandAlias] = Nil
|
||||||
|
override def execute (using command: InputCommand, event: Update): Unit =
|
||||||
|
val cxt = MessagingContext.extract(using event.message)
|
||||||
|
val cxtReplied = Option(event.message.replyToMessage).map(MessagingContext.extract(using _))
|
||||||
|
errorMessageMap.get(cxtReplied.map(_.toChatMessageKey).orNull) match
|
||||||
|
case Some(msg) =>
|
||||||
|
sendErrorMessage(msg, ErrorMessage.Types.Complex, Some(cxt))
|
||||||
|
case None =>
|
||||||
|
SendMessage(
|
||||||
|
cxt.bind_chat.id,
|
||||||
|
"Not a error message."
|
||||||
|
).replyToMessageId(cxt.bind_message.messageId)
|
||||||
|
.unsafeExecute
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,12 +2,14 @@ package cc.sukazyo.cono.morny.morny_misc
|
|||||||
|
|
||||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||||
import cc.sukazyo.cono.morny.core.bot.api.messages.MessagingContext
|
import cc.sukazyo.cono.morny.core.bot.api.messages.{ErrorMessage, MessagingContext}
|
||||||
|
import cc.sukazyo.cono.morny.core.bot.api.BotExtension.submit
|
||||||
|
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.Requests.unsafeExecute
|
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||||
import com.pengrad.telegrambot.model.{Message, Update}
|
import com.pengrad.telegrambot.model.{Message, 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, SendSticker}
|
||||||
import com.pengrad.telegrambot.TelegramBot
|
import com.pengrad.telegrambot.TelegramBot
|
||||||
|
|
||||||
class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
|
class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||||
@ -32,12 +34,33 @@ class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private def execute2 (message: Message, previousContext: MessagingContext.WithUserAndMessage): Unit = {
|
private def execute2 (message: Message, previousContext: MessagingContext.WithUserAndMessage): Unit = {
|
||||||
|
|
||||||
|
if (message.text == "oops")
|
||||||
|
SendMessage(
|
||||||
|
message.chat.id,
|
||||||
|
"A test error message will be generated."
|
||||||
|
).replyToMessageId(message.messageId)
|
||||||
|
.unsafeExecute
|
||||||
|
ErrorMessage(
|
||||||
|
_simple = SendSticker(
|
||||||
|
message.chat.id,
|
||||||
|
TelegramStickers.ID_404
|
||||||
|
).replyToMessageId(message.messageId),
|
||||||
|
_complex = SendMessage(
|
||||||
|
message.chat.id,
|
||||||
|
"Oops: There is just a test error."
|
||||||
|
).replyToMessageId(message.messageId)
|
||||||
|
)(using MessagingContext.extract(using message))
|
||||||
|
.submit
|
||||||
|
return;
|
||||||
|
|
||||||
SendMessage(
|
SendMessage(
|
||||||
message.chat.id,
|
message.chat.id,
|
||||||
// language=html
|
// language=html
|
||||||
"<b><u>Test command with following input:</u></b>\n" + message.text
|
"<b><u>Test command with following input:</u></b>\n" + message.text
|
||||||
).replyToMessageId(message.messageId).parseMode(ParseMode HTML)
|
).replyToMessageId(message.messageId).parseMode(ParseMode HTML)
|
||||||
.unsafeExecute
|
.unsafeExecute
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,6 +8,13 @@ import scala.jdk.CollectionConverters.*
|
|||||||
|
|
||||||
object TelegramParseEscape {
|
object TelegramParseEscape {
|
||||||
|
|
||||||
|
/** Encoded a [[String]] that make it can be used as plain texts in Telegram HTML.
|
||||||
|
*
|
||||||
|
* This method will just remove `&`, `<` and `>` characters and encode them to
|
||||||
|
* HTML entities.
|
||||||
|
*
|
||||||
|
* @since 1.0.0
|
||||||
|
*/
|
||||||
def escapeHtml (input: String): String =
|
def escapeHtml (input: String): String =
|
||||||
var process = input
|
var process = input
|
||||||
process = process.replaceAll("&", "&")
|
process = process.replaceAll("&", "&")
|
||||||
@ -15,6 +22,24 @@ object TelegramParseEscape {
|
|||||||
process = process.replaceAll(">", ">")
|
process = process.replaceAll(">", ">")
|
||||||
process
|
process
|
||||||
|
|
||||||
|
/** Transform a [[String]] encoded HTML document/fragment into a Telegram capable
|
||||||
|
* HTML fragment, make it can be used in sending Telegram message etc.
|
||||||
|
*
|
||||||
|
* This method will remove all unsupported HTML tags and attributes. Here is the
|
||||||
|
* specific rule list of how to process the tags:
|
||||||
|
*
|
||||||
|
* - If the tag is supported by Telegram HTML, it will be kept, and the inner HTML
|
||||||
|
* children items will be processed recursively.
|
||||||
|
* - If the tag is `<br>`, it will be converted to a newline character (`\n`). If
|
||||||
|
* there's any children in the `<br>` tag (which is not allowed in HTML), they
|
||||||
|
* will just be ignored.
|
||||||
|
* - If the tag is `<img>`, it will be removed. If the `alt` attribute is present,
|
||||||
|
* a string `"[$alt]"` will be used as the replacement text.
|
||||||
|
* - If the tag is any other tag, it will be removed, and the inner HTML children
|
||||||
|
* will be kept and processed recursively.
|
||||||
|
*
|
||||||
|
* @since 1.3.0
|
||||||
|
*/
|
||||||
def cleanupHtml (input: String): String =
|
def cleanupHtml (input: String): String =
|
||||||
import org.jsoup.nodes.*
|
import org.jsoup.nodes.*
|
||||||
val source = Jsoup.parse(input)
|
val source = Jsoup.parse(input)
|
||||||
@ -32,7 +57,7 @@ object TelegramParseEscape {
|
|||||||
// case _: (DataNode | XmlDeclaration | DocumentType | Comment) => ""
|
// case _: (DataNode | XmlDeclaration | DocumentType | Comment) => ""
|
||||||
// case elem: Element => elem.childNodes.asScala.map(f => toHtmlRaw(f)).mkString("")
|
// case elem: Element => elem.childNodes.asScala.map(f => toHtmlRaw(f)).mkString("")
|
||||||
|
|
||||||
def cleanupHtml (input: Seq[Node]): List[Node] =
|
private def cleanupHtml (input: Seq[Node]): List[Node] =
|
||||||
val result = mutable.ListBuffer.empty[Node]
|
val result = mutable.ListBuffer.empty[Node]
|
||||||
for (i <- input) {
|
for (i <- input) {
|
||||||
import org.jsoup.nodes.*
|
import org.jsoup.nodes.*
|
||||||
|
Loading…
Reference in New Issue
Block a user