mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-22 11:14:55 +08:00
refactor bot execute method etc.
- change Log.exceptionLog(throwable) to an extension method throwable.toLogString in UseThrowable - added SimpleCommandManager as the backend of both MornyCommandManager and UniMeowCommandManager - added Request().execute and Request().unsafeExecute() extensions. - change all the request execute using those extensions. - change some method with infix keyword. - change MornyTrusted methods using User/Chat object instead of a bare id. - update scala to 3.4.0-RC4 and fix infix warnings.
This commit is contained in:
parent
9cc8b49459
commit
8d04d6529c
16
build.sbt
16
build.sbt
@ -3,7 +3,7 @@ aether.AetherKeys.aetherOldVersionMethod := true
|
||||
ThisBuild / organization := "cc.sukazyo"
|
||||
ThisBuild / organizationName := "A.C. Sukazyo Eyre"
|
||||
|
||||
ThisBuild / scalaVersion := "3.3.1"
|
||||
ThisBuild / scalaVersion := "3.4.0-RC4"
|
||||
|
||||
resolvers ++= Seq(
|
||||
"-ws-releases" at "https://mvn.sukazyo.cc/releases"
|
||||
@ -56,6 +56,20 @@ lazy val root = (project in file("."))
|
||||
"-target", "17"
|
||||
),
|
||||
autoAPIMappings := true,
|
||||
apiMappings ++= {
|
||||
def mappingsFor(organization: String, names: List[String], location: String, revision: String => String = identity): Seq[(File, URL)] =
|
||||
for {
|
||||
entry: Attributed[File] <- (Compile / fullClasspath).value
|
||||
module: ModuleID <- entry.get(moduleID.key)
|
||||
if module.organization == organization
|
||||
if names.exists(module.name.startsWith)
|
||||
} yield entry.data -> url(location.format(revision(module.revision)))
|
||||
val mappings: Seq[(File, URL)] = Seq(
|
||||
mappingsFor("org.scala-lang", List("scala-library"), "https://scala-lang.org/api/%s/"),
|
||||
mappingsFor("com.github.pengrad", "java-telegram-bot-api"::Nil, "https://jitpack.io/com/github/pengrad/java-telegram-bot-api/6.3.0/javadoc/"),
|
||||
).flatten
|
||||
mappings.toMap
|
||||
},
|
||||
|
||||
assemblyMergeStrategy := {
|
||||
case module if module endsWith "module-info.class" => MergeStrategy.concat
|
||||
|
@ -8,7 +8,7 @@ object MornyConfiguration {
|
||||
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 VERSION = "2.0.0-alpha13"
|
||||
val VERSION = "2.0.0-alpha14"
|
||||
val VERSION_DELTA: Option[String] = None
|
||||
val CODENAME = "guanggu"
|
||||
|
||||
|
@ -4,14 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.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 cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{Chat, Message, User}
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
|
||||
|
||||
import scala.language.postfixOps
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class OnCallMe (using coeur: MornyCoeur) extends EventListener {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private val me = coeur.config.trustedMaster
|
||||
|
||||
@ -30,65 +30,70 @@ class OnCallMe (using coeur: MornyCoeur) extends EventListener {
|
||||
requestItem(update.message.from, "<b>Hana Paresu</b>")
|
||||
case "dinner" | "lunch" | "breakfast" | "meal" | "eating" | "安妮今天吃什么" =>
|
||||
requestLastDinner(update.message)
|
||||
case cc if cc startsWith "cc::" =>
|
||||
case cc if cc `startsWith` "cc::" =>
|
||||
requestCustom(update.message)
|
||||
case _ =>
|
||||
return;
|
||||
|
||||
if success then
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
update.message.chat.id,
|
||||
TelegramStickers ID_SENT
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
else
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
update.message.chat.id,
|
||||
TelegramStickers ID_501
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
|
||||
private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Boolean =
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
me,
|
||||
s"""request $itemHTML
|
||||
|from ${user.fullnameRefHTML}${if extra == null then "" else "\n"+extra}"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
true
|
||||
|
||||
private def requestLastDinner (req: Message): Boolean = {
|
||||
if coeur.config.dinnerChatId == -1 then return false
|
||||
var isAllowed = false
|
||||
var lastDinnerData: Message|Null = null
|
||||
if (coeur.trusted isTrusted_dinnerReader req.from.id) {
|
||||
if (coeur.trusted isTrust4dinner req.from) {
|
||||
// todo: have issues
|
||||
// i dont want to test it anymore... it might be deprecated soon
|
||||
lastDinnerData = (coeur.account exec GetChat(coeur.config.dinnerChatId)).chat.pinnedMessage
|
||||
val sendResp = coeur.account exec ForwardMessage(
|
||||
lastDinnerData = GetChat(coeur.config.dinnerChatId).unsafeExecute.chat.pinnedMessage
|
||||
val sendResp = ForwardMessage(
|
||||
req.from.id,
|
||||
lastDinnerData.forwardFromChat.id,
|
||||
lastDinnerData.forwardFromMessageId
|
||||
)
|
||||
).unsafeExecute
|
||||
import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
||||
def lastDinner_dateMillis: EpochMillis = EpochMillis fromEpochSeconds lastDinnerData.forwardDate
|
||||
coeur.account exec SendMessage(
|
||||
def lastDinner_dateMillis: EpochMillis = EpochMillis fromSeconds lastDinnerData.forwardDate
|
||||
SendMessage(
|
||||
req.from.id,
|
||||
"<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>".formatted(
|
||||
h(formatDate(lastDinner_dateMillis, 8)),
|
||||
h(formatDuration(System.currentTimeMillis - lastDinner_dateMillis))
|
||||
)
|
||||
).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId)
|
||||
.unsafeExecute
|
||||
isAllowed = true
|
||||
} else {
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
req.from.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(req.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
import Math.abs
|
||||
requestItem(
|
||||
@ -100,7 +105,8 @@ class OnCallMe (using coeur: MornyCoeur) extends EventListener {
|
||||
|
||||
private def requestCustom (message: Message): Boolean =
|
||||
requestItem(message.from, "<u>[???]</u>")
|
||||
coeur.account exec ForwardMessage(me, message.chat.id, message.messageId)
|
||||
ForwardMessage(me, message.chat.id, message.messageId)
|
||||
.unsafeExecute
|
||||
true
|
||||
|
||||
}
|
||||
|
@ -3,16 +3,18 @@ package cc.sukazyo.cono.morny.call_me
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.*
|
||||
import com.pengrad.telegrambot.model.{Chat, Message, MessageEntity}
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{GetChat, SendMessage, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.language.postfixOps
|
||||
import scala.util.matching.Regex
|
||||
|
||||
class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private val REGEX_MSG_SENDREQ_DATA_HEAD: Regex = "^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$"r
|
||||
|
||||
@ -25,12 +27,12 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
def toSendMessage (target_override: Long|Null = null): SendMessage =
|
||||
val useTarget = if target_override == null then targetId else target_override
|
||||
val sendMessage = SendMessage(useTarget, message)
|
||||
if entities ne null then sendMessage.entities(entities:_*)
|
||||
if entities ne null then sendMessage.entities(entities*)
|
||||
if parseMode ne null then sendMessage.parseMode(parseMode)
|
||||
sendMessage
|
||||
}
|
||||
private object MessageToSend:
|
||||
def from (raw: Message): MessageToSend = {
|
||||
infix def from (raw: Message): MessageToSend = {
|
||||
raw.text match
|
||||
case REGEX_MSG_SENDREQ_DATA_HEAD(_target, _parseMode, _body) =>
|
||||
val target = _target toLong
|
||||
@ -57,15 +59,15 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
|
||||
val message = update.message
|
||||
|
||||
if message.chat.`type` != Chat.Type.Private then return;
|
||||
if message.text eq null then return;
|
||||
if !(message.text startsWith "*msg") then return;
|
||||
if message.chat.`type` != Chat.Type.Private then return
|
||||
if message.text eq null then return
|
||||
if !(message.text `startsWith` "*msg") then return
|
||||
|
||||
if (!(coeur.trusted isTrusted message.from.id))
|
||||
coeur.account exec SendSticker(
|
||||
if (!(coeur.trusted isTrust message.from))
|
||||
SendSticker(
|
||||
message.chat.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(message.messageId)
|
||||
).replyToMessageId(message.messageId).unsafeExecute
|
||||
event.setEventOk
|
||||
return;
|
||||
|
||||
@ -74,21 +76,23 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
if (message.replyToMessage eq null) { answer404; return }
|
||||
val messageToSend = MessageToSend from message.replyToMessage
|
||||
if ((messageToSend eq null) || (messageToSend.message eq null)) { answer404; return }
|
||||
val sendResponse = coeur.account execute messageToSend.toSendMessage()
|
||||
val sendResponse = messageToSend.toSendMessage().execute
|
||||
|
||||
if (sendResponse isOk) {
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
update.message.chat.id,
|
||||
TelegramStickers ID_SENT
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
} else {
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
// language=html
|
||||
s"""<b><u>${sendResponse.errorCode} FAILED</u></b>
|
||||
|<code>${sendResponse.description}</code>"""
|
||||
.stripMargin
|
||||
).replyToMessageId(update.message.messageId).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
event.setEventOk
|
||||
@ -101,14 +105,14 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
if (message.text == "*msg")
|
||||
if message.replyToMessage eq null then { answer404; return }
|
||||
else message.replyToMessage
|
||||
else if (message.text startsWith "*msg")
|
||||
else if (message.text `startsWith` "*msg")
|
||||
message
|
||||
else { answer404; return }
|
||||
val _toSend = MessageToSend from raw
|
||||
if _toSend eq null then { answer404; return }
|
||||
else _toSend
|
||||
|
||||
val targetChatResponse = coeur.account execute GetChat(messageToSend.targetId)
|
||||
val targetChatResponse = GetChat(messageToSend.targetId).execute
|
||||
if (targetChatResponse isOk) {
|
||||
def getChatDescriptionHTML (chat: Chat): String =
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
|
||||
@ -117,41 +121,46 @@ class OnCallMsgSend (using coeur: MornyCoeur) extends EventListener {
|
||||
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}"""
|
||||
.stripMargin
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
getChatDescriptionHTML(targetChatResponse.chat)
|
||||
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
} else {
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
// language=html
|
||||
s"""<b><u>${targetChatResponse.errorCode} FAILED</u></b>
|
||||
|<code>${targetChatResponse.description}</code>"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
if messageToSend.message eq null then { answer404; return }
|
||||
val testSendResponse = coeur.account execute
|
||||
val testSendResponse =
|
||||
messageToSend.toSendMessage(update.message.chat.id).replyToMessageId(update.message.messageId)
|
||||
.execute
|
||||
if (!(testSendResponse isOk))
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
// language=html
|
||||
s"""<b><u>${testSendResponse.errorCode}</u> FAILED</b>
|
||||
|<code>${testSendResponse.description}</code>"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
|
||||
private def answer404 (using event: EventEnv): Unit =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.update.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.update.message.messageId)
|
||||
.unsafeExecute
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
|
@ -4,8 +4,6 @@ import cc.sukazyo.cono.morny.core.internal.logging.{MornyFormatterConsole, Morny
|
||||
import cc.sukazyo.messiva.appender.ConsoleAppender
|
||||
import cc.sukazyo.messiva.log.LogLevels
|
||||
|
||||
import java.io.{PrintWriter, StringWriter}
|
||||
|
||||
object Log {
|
||||
|
||||
val logger: MornyLoggerBase = MornyLoggerBase(
|
||||
@ -13,7 +11,7 @@ object Log {
|
||||
MornyFormatterConsole()
|
||||
)
|
||||
)
|
||||
logger minLevel LogLevels.INFO
|
||||
logger.minLevel(LogLevels.INFO)
|
||||
|
||||
def debug: Boolean = logger.levelSetting.minLevel.level <= LogLevels.DEBUG.level
|
||||
|
||||
@ -21,9 +19,4 @@ object Log {
|
||||
if is then logger.minLevel(LogLevels.ALL)
|
||||
else logger.minLevel(LogLevels.INFO)
|
||||
|
||||
def exceptionLog (e: Throwable): String =
|
||||
val stackTrace = StringWriter()
|
||||
e printStackTrace PrintWriter(stackTrace)
|
||||
stackTrace toString
|
||||
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import java.io.IOException
|
||||
object MornyAbout {
|
||||
|
||||
val MORNY_PREVIEW_IMAGE_ASCII: String =
|
||||
try { MornyAssets.pack getResource "texts/server-hello.txt" readAsString }
|
||||
try { MornyAssets.pack `getResource` "texts/server-hello.txt" readAsString }
|
||||
catch case e: IOException =>
|
||||
throw RuntimeException("Cannot read MORNY_PREVIEW_IMAGE_ASCII from assets pack", e)
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cc.sukazyo.cono.morny.core
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
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.event.{MornyOnInlineQuery, MornyOnTelegramCommand, MornyOnUpdateTimestampOffsetLock}
|
||||
@ -11,6 +11,7 @@ import cc.sukazyo.cono.morny.util.schedule.Scheduler
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import cc.sukazyo.cono.morny.util.time.WatchDog
|
||||
import cc.sukazyo.cono.morny.util.GivenContext
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
import com.pengrad.telegrambot.request.GetMe
|
||||
|
||||
@ -115,7 +116,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
val externalContext: GivenContext = GivenContext()
|
||||
import cc.sukazyo.cono.morny.util.dataview.Table.format as fmtTable
|
||||
logger info
|
||||
logger `info`
|
||||
s"""The following Modules have been added to current Morny:
|
||||
|${fmtTable(
|
||||
("Module ID" :: "Module Name" :: "Module Version" :: Nil)::Nil :::
|
||||
@ -125,19 +126,19 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
///>>> BLOCK START instance configure & startup stage 1
|
||||
|
||||
logger info "Coeur starting..."
|
||||
logger `info` "Coeur starting..."
|
||||
private var initializeContext = GivenContext()
|
||||
|
||||
import cc.sukazyo.cono.morny.util.StringEnsure.deSensitive
|
||||
logger info s"args key:\n ${config.telegramBotKey deSensitive 4}"
|
||||
logger `info` s"args key:\n ${config.telegramBotKey.deSensitive(4)}"
|
||||
if config.telegramBotUsername ne null then
|
||||
logger info s"login as:\n ${config.telegramBotUsername}"
|
||||
logger `info` s"login as:\n ${config.telegramBotUsername}"
|
||||
|
||||
private val __loginResult: LoginResult = login() match
|
||||
case some: Some[LoginResult] => some.get
|
||||
case None =>
|
||||
logger error "Login to bot failed."
|
||||
System exit -1
|
||||
logger `error` "Login to bot failed."
|
||||
System `exit` -1
|
||||
throw RuntimeException()
|
||||
initializeContext << __loginResult
|
||||
|
||||
@ -221,7 +222,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
val watchDog: WatchDog = WatchDog("watch-dog", 1000, 1500, { (consumed, _) =>
|
||||
import cc.sukazyo.cono.morny.util.CommonFormat.formatDuration as f
|
||||
logger warn
|
||||
logger `warn`
|
||||
s"""Can't keep up! is the server overloaded or host machine fall asleep?
|
||||
| current tick takes ${f(consumed)} to complete.""".stripMargin
|
||||
tasks.notifyIt()
|
||||
@ -238,9 +239,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
///>>> BLOCK START instance configure & startup stage 2
|
||||
|
||||
logger info "done initialize."
|
||||
logger `info` "done initialize."
|
||||
if testRun then
|
||||
logger info "done test run, exiting."
|
||||
logger `info` "done test run, exiting."
|
||||
this.exit(0, TestRun)
|
||||
|
||||
// Coeur Starting Pre
|
||||
@ -250,10 +251,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
modules.foreach(it => it.onStarting(OnStartingContext(
|
||||
initializeContext)))
|
||||
|
||||
logger info "start http server"
|
||||
logger `info` "start http server"
|
||||
val http: HttpServer = _httpServerContext.start
|
||||
_httpServerContext = null
|
||||
logger info "start telegram event listening"
|
||||
logger `info` "start telegram event listening"
|
||||
import com.pengrad.telegrambot.TelegramException
|
||||
account.setUpdatesListener(eventManager, (e: TelegramException) => {
|
||||
|
||||
@ -267,10 +268,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
if (e.response != null) {
|
||||
import com.google.gson.GsonBuilder
|
||||
logger error
|
||||
logger `error`
|
||||
s"""Failed get updates: ${e.getMessage}
|
||||
| server responses:
|
||||
|${GsonBuilder().setPrettyPrinting().create.toJson(e.response) indent 4}
|
||||
|${GsonBuilder().setPrettyPrinting().create.toJson(e.response).indent(4)}
|
||||
|""".stripMargin
|
||||
externalContext.consume[MornyReport](_.exception(e, "Failed get updates."))
|
||||
}
|
||||
@ -291,11 +292,11 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
current = current.getCause
|
||||
log += s" caused by: ${current.getClass.getSimpleName}: ${current.getMessage}"
|
||||
}
|
||||
logger error Message(log mkString "\n")
|
||||
logger `error` Message(log mkString "\n")
|
||||
case e_other =>
|
||||
logger error
|
||||
logger `error`
|
||||
s"""Failed get updates:
|
||||
|${exceptionLog(e_other) indent 3}""".stripMargin
|
||||
|${e_other.toLogString `indent` 3}""".stripMargin
|
||||
externalContext.consume[MornyReport](_.exception(e_other, "Failed get updates."))
|
||||
}
|
||||
|
||||
@ -306,17 +307,17 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
initializeContext)))
|
||||
|
||||
if config.commandLoginRefresh then
|
||||
logger info "resetting telegram command list"
|
||||
logger `info` "resetting telegram command list"
|
||||
commands.automaticTGListUpdate()
|
||||
|
||||
initializeContext = null
|
||||
logger info "Coeur start complete."
|
||||
logger `info` "Coeur start complete."
|
||||
|
||||
///<<< BLOCK END instance configure & startup stage 2
|
||||
|
||||
def saveDataAll(): Unit = {
|
||||
modules.foreach(it => it.onRoutineSavingData)
|
||||
logger notice "done all save action."
|
||||
logger `notice` "done all save action."
|
||||
}
|
||||
|
||||
private def exitCleanup (): Unit = {
|
||||
@ -325,9 +326,9 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
modules.foreach(it => it.onExiting)
|
||||
|
||||
account.removeGetUpdatesListener()
|
||||
logger info "stopped bot update listener"
|
||||
logger `info` "stopped bot update listener"
|
||||
tasks.waitForStop()
|
||||
logger info s"morny tasks stopped: remains ${tasks.amount} tasks not be executed"
|
||||
logger `info` s"morny tasks stopped: remains ${tasks.amount} tasks not be executed"
|
||||
|
||||
// Morny Exiting Post
|
||||
if config.commandLogoutClear then
|
||||
@ -335,10 +336,10 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
modules.foreach(it => it.onExitingPost)
|
||||
|
||||
account.shutdown()
|
||||
logger info "stopped bot account"
|
||||
logger `info` "stopped bot account"
|
||||
// Morny Exited
|
||||
modules.foreach(it => it.onExited)
|
||||
logger info "done exit cleanup\nMorny will EXIT now"
|
||||
logger `info` "done exit cleanup\nMorny will EXIT now"
|
||||
|
||||
}
|
||||
|
||||
@ -348,7 +349,7 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
def exit (status: Int, reason: AnyRef): Unit =
|
||||
whileExit_reason = Some(reason)
|
||||
System exit status
|
||||
System `exit` status
|
||||
|
||||
private case class LoginResult(account: TelegramBot, username: String, userid: Long)
|
||||
|
||||
@ -358,15 +359,15 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
var api_bot = config.telegramBotApiServer
|
||||
var api_file = config.telegramBotApiServer4File
|
||||
if (api_bot ne null)
|
||||
if api_bot endsWith "/" then api_bot = api_bot dropRight 1
|
||||
if !(api_bot endsWith "/bot") then api_bot += "/bot"
|
||||
if api_bot `endsWith` "/" then api_bot = api_bot dropRight 1
|
||||
if !(api_bot `endsWith` "/bot") then api_bot += "/bot"
|
||||
builder.apiUrl(api_bot)
|
||||
if (api_file ne null)
|
||||
if api_file endsWith "/file/" then api_file = api_file dropRight 1
|
||||
if !(api_file endsWith "/file/bot") then api_file += "/file/bot"
|
||||
if api_file `endsWith` "/file/" then api_file = api_file dropRight 1
|
||||
if !(api_file `endsWith` "/file/bot") then api_file += "/file/bot"
|
||||
builder.apiUrl(api_bot)
|
||||
if ((api_bot ne null) || (api_file ne null))
|
||||
logger info
|
||||
logger `info`
|
||||
s"""Telegram bot api set to:
|
||||
|- bot: $api_bot
|
||||
|- file: $api_file"""
|
||||
@ -374,21 +375,22 @@ class MornyCoeur (modules: List[MornyModule])(using val config: MornyConfig)(tes
|
||||
|
||||
val account = builder build
|
||||
|
||||
logger info "Trying to login..."
|
||||
logger `info` "Trying to login..."
|
||||
boundary[Option[LoginResult]] {
|
||||
for (i <- 0 to 3) {
|
||||
if i > 0 then logger info "retrying..."
|
||||
if i > 0 then logger `info` "retrying..."
|
||||
try {
|
||||
val remote = (account execute GetMe()).user
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute
|
||||
val remote = GetMe().execute(using account).user
|
||||
if ((config.telegramBotUsername ne null) && config.telegramBotUsername != remote.username)
|
||||
throw RuntimeException(s"Required the bot @${config.telegramBotUsername} but @${remote.username} logged in")
|
||||
logger info s"Succeed logged in to @${remote.username}"
|
||||
logger `info` s"Succeed logged in to @${remote.username}"
|
||||
break(Some(LoginResult(account, remote.username, remote.id)))
|
||||
} catch
|
||||
case r: boundary.Break[Option[LoginResult]] => throw r
|
||||
case e =>
|
||||
logger error
|
||||
s"""${exceptionLog(e)}
|
||||
logger `error`
|
||||
s"""${e.toLogString}
|
||||
|login failed"""
|
||||
.stripMargin
|
||||
}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package cc.sukazyo.cono.morny.core
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.internal.BuildConfigField
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import cc.sukazyo.cono.morny.util.FileUtils
|
||||
import cc.sukazyo.cono.morny.BuildConfig
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.URISyntaxException
|
||||
@ -38,7 +39,7 @@ object MornySystem {
|
||||
case _: (IOException|URISyntaxException) =>
|
||||
"<non-jar-runtime>"
|
||||
case n: NoSuchAlgorithmException =>
|
||||
logger error exceptionLog(n)
|
||||
logger `error` n.toLogString
|
||||
// MornyReport.exception(n, "<coeur-md5/calculation-error>") // todo: will not implemented
|
||||
"<calculation-error>"
|
||||
}
|
||||
|
@ -5,20 +5,33 @@ import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.{LimboChat, LimboUser
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Chat.*
|
||||
import com.pengrad.telegrambot.model.ChatMember.Status
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
import com.pengrad.telegrambot.model.User
|
||||
|
||||
class MornyTrusted (using coeur: MornyCoeur)(using config: MornyConfig) {
|
||||
|
||||
if config.trustedMaster == -1 then
|
||||
logger warn "You have not set your Morny's master.\n it may have some issues on controlling your bot."
|
||||
logger `warn` "You have not set your Morny's master.\n it may have some issues on controlling your bot."
|
||||
|
||||
def isTrusted (userId: Long): Boolean =
|
||||
/** If the user can be trusted.
|
||||
* @since 2.0.0
|
||||
* @param user The user that will be check if it is in this trust list. Only user's id will be used,
|
||||
* so if you don't have a user instance, you can safely use [[LimboUser]].
|
||||
* @return `true` if this user can be trusted, `false` otherwise.
|
||||
*/
|
||||
infix def isTrust (user: User): Boolean =
|
||||
given TelegramBot = coeur.account
|
||||
if userId == config.trustedMaster then true
|
||||
if user.id == config.trustedMaster then true
|
||||
else if config.trustedChat == -1 then false
|
||||
else LimboChat(config.trustedChat) memberHasPermission(LimboUser(userId), Status.administrator)
|
||||
else LimboChat(config.trustedChat).memberHasPermission(user, Status.administrator)
|
||||
|
||||
def isTrusted_dinnerReader (userId: Long): Boolean =
|
||||
if userId == config.trustedMaster then true
|
||||
else config.dinnerTrustedReaders contains userId
|
||||
/** If this user can be trusted to read the dinner messages.
|
||||
* @since 2.0.0
|
||||
* @param user The user that will be check if it is in this trust list. Only user's id will be used,
|
||||
* so if you don't have a user instance, you can safely use [[LimboUser]].
|
||||
* @return `true` if this user can read dinners, `false` otherwise.
|
||||
*/
|
||||
infix def isTrust4dinner (user: User): Boolean =
|
||||
if user.id == config.trustedMaster then true
|
||||
else config.dinnerTrustedReaders `contains` user.id
|
||||
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ import scala.collection.mutable.ArrayBuffer
|
||||
object ServerMain {
|
||||
|
||||
val tz: TimeZone = TimeZone getDefault
|
||||
val tz_offset: ZoneOffset = ZoneOffset ofTotalSeconds(tz.getRawOffset / 1000)
|
||||
val tz_offset: ZoneOffset = ZoneOffset `ofTotalSeconds` (tz.getRawOffset / 1000)
|
||||
|
||||
private val THREAD_MORNY_INIT: String = "morny-init"
|
||||
|
||||
@ -54,7 +54,7 @@ object ServerMain {
|
||||
case "--report-to" => i += 1; config.reportToChat = args(i) toLong
|
||||
case "--report-zone" => i += 1; config.reportZone = TimeZone.getTimeZone(args(i))
|
||||
|
||||
case "--trusted-reader-dinner" | "-trsd" => i += 1; config.dinnerTrustedReaders add(args(i) toLong)
|
||||
case "--trusted-reader-dinner" | "-trsd" => i += 1; config.dinnerTrustedReaders `add` (args(i) toLong)
|
||||
case "--dinner-chat" | "-chd" => i += 1; config.dinnerChatId = args(i) toLong
|
||||
|
||||
case "--http-listen-port" | "-hp" =>
|
||||
@ -67,8 +67,8 @@ object ServerMain {
|
||||
config.medicationTimerUseTimezone = ZoneOffset.ofHours(args(i) toInt)
|
||||
case "--medication-notify-times" | "-medt" =>
|
||||
i += 1
|
||||
for (u <- args(i) split ",") {
|
||||
config.medicationNotifyAt add(u toInt)
|
||||
for (u <- args(i) `split` ",") {
|
||||
config.medicationNotifyAt `add` (u toInt)
|
||||
}
|
||||
|
||||
case "--auto-cmd-list" | "-ca" => config.commandLoginRefresh = true
|
||||
@ -87,8 +87,8 @@ object ServerMain {
|
||||
var propToken: String = null
|
||||
var propTokenKey: String = null
|
||||
for (iKey <- MornyConfig.PROP_TOKEN_KEY) {
|
||||
if ((System getenv iKey) != null) {
|
||||
propToken = System getenv iKey
|
||||
if ((System `getenv` iKey) != null) {
|
||||
propToken = System `getenv` iKey
|
||||
propTokenKey = iKey
|
||||
}
|
||||
}
|
||||
@ -98,27 +98,27 @@ object ServerMain {
|
||||
/// process startup params - like startup mode
|
||||
///
|
||||
|
||||
if (showHello) logger info MornyAbout.MORNY_PREVIEW_IMAGE_ASCII
|
||||
if (showHello) logger `info` MornyAbout.MORNY_PREVIEW_IMAGE_ASCII
|
||||
if (mode_echoHello) return
|
||||
|
||||
if (unknownArgs.nonEmpty) logger warn
|
||||
if (unknownArgs.nonEmpty) logger `warn`
|
||||
s"""Can't understand arg to some meaning
|
||||
| ${unknownArgs mkString "\n "}"""
|
||||
.stripMargin
|
||||
if (deprecatedArgs.nonEmpty) logger warn
|
||||
if (deprecatedArgs.nonEmpty) logger `warn`
|
||||
s"""Those arguments have been deprecated:
|
||||
| ${deprecatedArgs map((d, n) => s"$d : use $n instead") mkString "\n "}
|
||||
|""".stripMargin
|
||||
|
||||
if (Log debug)
|
||||
logger warn
|
||||
logger `warn`
|
||||
"""Debug log output enabled.
|
||||
| It may lower your performance, make sure that you are not in production environment."""
|
||||
.stripMargin
|
||||
|
||||
if (mode_echoVersion) {
|
||||
|
||||
logger info
|
||||
logger `info`
|
||||
s"""Morny Cono Version
|
||||
|- version :
|
||||
| Morny ${MornySystem.CODENAME toUpperCase}
|
||||
@ -148,14 +148,14 @@ object ServerMain {
|
||||
|
||||
}
|
||||
|
||||
logger info
|
||||
logger `info`
|
||||
s"""ServerMain.java Loaded >>>
|
||||
|- version ${MornySystem.VERSION_FULL}
|
||||
|- Morny ${MornySystem.CODENAME toUpperCase}
|
||||
|- <${MornySystem.getJarMD5}> [${MornySystem.CODE_TIMESTAMP}]""".stripMargin
|
||||
|
||||
// due to [[MornyFormatterConsole]] will use a localized time, it will output to the log
|
||||
logger info s"logging time will use time-zone ${tz.getID} ($tz_offset)"
|
||||
logger `info` s"logging time will use time-zone ${tz.getID} ($tz_offset)"
|
||||
|
||||
///
|
||||
/// Check Coeur arguments
|
||||
@ -164,10 +164,10 @@ object ServerMain {
|
||||
|
||||
if (propToken != null) {
|
||||
config.telegramBotKey = propToken
|
||||
logger info s"Parameter <token> set by EnvVar $$$propTokenKey"
|
||||
logger `info` s"Parameter <token> set by EnvVar $$$propTokenKey"
|
||||
}
|
||||
|
||||
Thread.currentThread setName THREAD_MORNY_INIT
|
||||
Thread.currentThread `setName` THREAD_MORNY_INIT
|
||||
|
||||
try
|
||||
MornyCoeur(
|
||||
|
@ -1,15 +1,15 @@
|
||||
package cc.sukazyo.cono.morny.core.bot.api
|
||||
|
||||
import cc.sukazyo.cono.morny.core.{Log, MornyCoeur}
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.UpdatesListener
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.language.postfixOps
|
||||
|
||||
/** Contains a [[scala.collection.mutable.Queue]] of [[EventListener]], and delivery telegram [[Update]].
|
||||
*
|
||||
@ -21,13 +21,16 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
|
||||
|
||||
private val listeners = mutable.Queue.empty[EventListener]
|
||||
|
||||
infix def register (listener: EventListener): Unit =
|
||||
this.listeners += listener
|
||||
|
||||
def register (listeners: EventListener*): Unit =
|
||||
this.listeners ++= listeners
|
||||
|
||||
private class EventRunner (using update: Update) extends Thread {
|
||||
this setName s"upd-${update.updateId()}-nn"
|
||||
this `setName` s"upd-${update.updateId()}-nn"
|
||||
private def updateThreadName (t: String): Unit =
|
||||
this setName s"upd-${update.updateId()}-$t"
|
||||
this `setName` s"upd-${update.updateId()}-$t"
|
||||
|
||||
override def run (): Unit = {
|
||||
|
||||
@ -80,15 +83,15 @@ class EventListenerManager (using coeur: MornyCoeur) extends UpdatesListener {
|
||||
} catch case e => {
|
||||
val errorMessage = StringBuilder()
|
||||
errorMessage ++= "Event throws unexpected exception:\n"
|
||||
errorMessage ++= (exceptionLog(e) indent 4)
|
||||
errorMessage ++= (e.toLogString `indent` 4)
|
||||
e match
|
||||
case actionFailed: EventRuntimeException.ActionFailed =>
|
||||
errorMessage ++= "\ntg-api action: response track: "
|
||||
errorMessage ++= (GsonBuilder().setPrettyPrinting().create().toJson(
|
||||
actionFailed.response
|
||||
) indent 4) ++= "\n"
|
||||
) `indent` 4) ++= "\n"
|
||||
case _ =>
|
||||
logger error errorMessage.toString
|
||||
logger `error` errorMessage.toString
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "on event running"))
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
package cc.sukazyo.cono.morny.core.bot.api
|
||||
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
|
||||
import javax.annotation.Nullable
|
||||
|
||||
trait ITelegramQuery {
|
||||
|
||||
def query (event: Update): List[InlineQueryUnit[_]] | Null
|
||||
def query (event: Update): List[InlineQueryUnit[?]] | Null
|
||||
|
||||
}
|
||||
|
@ -2,13 +2,12 @@ package cc.sukazyo.cono.morny.core.bot.api
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ISimpleCommand, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{BotCommand, DeleteMyCommands, Update}
|
||||
import com.pengrad.telegrambot.request.{SendSticker, SetMyCommands}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.collection.mutable
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
@ -17,14 +16,8 @@ import scala.language.postfixOps
|
||||
object MornyCommandManager:
|
||||
type CommandMap = mutable.SeqMap[String, ISimpleCommand]
|
||||
|
||||
class MornyCommandManager (using coeur: MornyCoeur) {
|
||||
|
||||
private val commands: CommandMap = mutable.SeqMap.empty
|
||||
def register [T <: ISimpleCommand] (commands: T*): Unit =
|
||||
for (i <- commands)
|
||||
this.commands += (i.name -> i)
|
||||
for (alias <- i.aliases)
|
||||
this.commands += (alias.name -> i)
|
||||
class MornyCommandManager (using coeur: MornyCoeur) extends SimpleCommandManager {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
def execute (using command: InputCommand, event: Update): Boolean = {
|
||||
if (commands contains command.command)
|
||||
@ -36,25 +29,28 @@ class MornyCommandManager (using coeur: MornyCoeur) {
|
||||
private def nonCommandExecutable (using command: InputCommand, event: Update): Boolean = {
|
||||
if command.target eq null then false
|
||||
else
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
true
|
||||
}
|
||||
|
||||
def automaticTGListUpdate (): Unit = {
|
||||
val listing = commands_toTelegramList
|
||||
automaticTGListRemove()
|
||||
coeur.account exec SetMyCommands(listing:_*)
|
||||
logger notice
|
||||
SetMyCommands(listing*)
|
||||
.unsafeExecute
|
||||
logger `notice`
|
||||
s"""automatic updated telegram command list :
|
||||
|${commandsTelegramList_toString(listing)}""".stripMargin
|
||||
}
|
||||
|
||||
def automaticTGListRemove (): Unit = {
|
||||
coeur.account exec DeleteMyCommands()
|
||||
logger notice "cleaned up command list"
|
||||
DeleteMyCommands()
|
||||
.unsafeExecute
|
||||
logger `notice` "cleaned up command list"
|
||||
}
|
||||
|
||||
private def commandsTelegramList_toString (list: Array[BotCommand]): String =
|
||||
|
@ -10,13 +10,16 @@ class MornyQueryManager (using MornyCoeur) {
|
||||
|
||||
private val queries = mutable.Queue.empty[ITelegramQuery]
|
||||
|
||||
infix def register (query: ITelegramQuery): Unit =
|
||||
this.queries += query
|
||||
|
||||
def register (queries: ITelegramQuery*): Unit =
|
||||
this.queries ++= queries
|
||||
|
||||
def query (event: Update): List[InlineQueryUnit[_]] = {
|
||||
val results = ListBuffer[InlineQueryUnit[_]]()
|
||||
def query (event: Update): List[InlineQueryUnit[?]] = {
|
||||
val results = ListBuffer[InlineQueryUnit[?]]()
|
||||
for (instance <- queries) {
|
||||
val r = instance query event
|
||||
val r = instance `query` event
|
||||
if (r != null) results ++= r
|
||||
}
|
||||
results.result()
|
||||
|
@ -0,0 +1,21 @@
|
||||
package cc.sukazyo.cono.morny.core.bot.api
|
||||
|
||||
import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
trait SimpleCommandManager {
|
||||
|
||||
protected val commands: CommandMap = mutable.SeqMap.empty
|
||||
|
||||
protected def doRegister [T <: ISimpleCommand](it: T): Unit =
|
||||
this.commands += (it.name -> it)
|
||||
for (alias <- it.aliases)
|
||||
this.commands += (alias.name -> it)
|
||||
|
||||
infix def register [T <: ISimpleCommand] (command: T): Unit =
|
||||
doRegister(command)
|
||||
def register [T <: ISimpleCommand] (commands: T*): Unit =
|
||||
for (command <- commands) doRegister(command)
|
||||
|
||||
}
|
@ -5,28 +5,30 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{Chat, Update}
|
||||
import com.pengrad.telegrambot.request.{DeleteMessage, GetChatMember, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "r"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
|
||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||
|
||||
logger debug "executing command /r"
|
||||
if (event.message.replyToMessage == null) return;
|
||||
logger trace "message is a reply"
|
||||
if (event.message.replyToMessage.from.id != coeur.userid) return;
|
||||
logger trace "message replied is from me"
|
||||
if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return;
|
||||
logger trace "message is not outdated(48 hrs ago)"
|
||||
logger `debug` "executing command /r"
|
||||
if (event.message.replyToMessage == null) return
|
||||
logger `trace` "message is a reply"
|
||||
if (event.message.replyToMessage.from.id != coeur.userid) return
|
||||
logger `trace` "message replied is from me"
|
||||
if (System.currentTimeMillis/1000 - event.message.replyToMessage.date > 48*60*60) return
|
||||
logger `trace` "message is not outdated(48 hrs ago)"
|
||||
|
||||
val isTrusted = coeur.trusted isTrusted event.message.from.id
|
||||
val isTrusted = coeur.trusted isTrust event.message.from
|
||||
// todo:
|
||||
// it does not work. due to the Telegram Bot API doesn't provide
|
||||
// nested replyToMessage, so currently the trusted check by
|
||||
@ -38,22 +40,22 @@ class DirectMsgClear (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
|
||||
if (isTrusted || _isReplyTrusted) {
|
||||
|
||||
coeur.account exec DeleteMessage(
|
||||
DeleteMessage(
|
||||
event.message.chat.id, event.message.replyToMessage.messageId
|
||||
)
|
||||
).unsafeExecute
|
||||
|
||||
def _isPrivate: Boolean = event.message.chat.`type` == Chat.Type.Private
|
||||
def _isPermission: Boolean =
|
||||
(coeur.account exec GetChatMember(event.message.chat.id, event.message.from.id))
|
||||
GetChatMember(event.message.chat.id, event.message.from.id).unsafeExecute
|
||||
.chatMember.canDeleteMessages
|
||||
if (_isPrivate || _isPermission) {
|
||||
coeur.account exec DeleteMessage(event.message.chat.id, event.message.messageId)
|
||||
DeleteMessage(event.message.chat.id, event.message.messageId).unsafeExecute
|
||||
}
|
||||
|
||||
} else coeur.account exec SendSticker(
|
||||
} else SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(event.message.messageId)
|
||||
).replyToMessageId(event.message.messageId).unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -5,13 +5,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.core.bot.api.ICommandAlias.ListedAlias
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendSticker
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class MornyHellos (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
object On extends ITelegramCommand {
|
||||
|
||||
@ -21,10 +23,11 @@ class MornyHellos (using coeur: MornyCoeur) {
|
||||
override val description: String = "检查是否在线"
|
||||
|
||||
override def execute (using command: InputCommand, event: Update): Unit =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_ONLINE_STATUS_RETURN
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
@ -36,10 +39,11 @@ class MornyHellos (using coeur: MornyCoeur) {
|
||||
override val description: String = "打招呼"
|
||||
|
||||
override def execute (using command: InputCommand, event: Update): Unit =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_HELLO
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,21 +4,21 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||
import cc.sukazyo.cono.morny.data.MornyInformation.{getAboutPic, getMornyAboutLinksHTML}
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.SendPhoto
|
||||
|
||||
import scala.language.postfixOps
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "start"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
|
||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||
|
||||
coeur.account exec new SendPhoto(
|
||||
SendPhoto(
|
||||
event.message.chat.id,
|
||||
getAboutPic
|
||||
).caption(
|
||||
@ -32,6 +32,7 @@ class MornyInfoOnStart (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
|(你可以随时通过 /info 重新获得这些信息)"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -8,16 +8,16 @@ import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||
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.InputCommand
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import java.lang.System
|
||||
import scala.language.postfixOps
|
||||
|
||||
// todo: maybe move some utils method outside
|
||||
class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private case object Subs {
|
||||
val STICKERS = "stickers"
|
||||
@ -43,7 +43,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
val action: String = command.args(0)
|
||||
|
||||
action match {
|
||||
case s if s startsWith Subs.STICKERS => echoStickers
|
||||
case s if s `startsWith` Subs.STICKERS => echoStickers
|
||||
case Subs.RUNTIME => echoRuntime
|
||||
case Subs.VERSION | Subs.VERSION_2 => echoVersion
|
||||
case Subs.TASKS => echoTasksStatus
|
||||
@ -54,7 +54,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
}
|
||||
|
||||
private def echoInfo (chatId: Long, replyTo: Int): Unit = {
|
||||
coeur.account exec new SendPhoto(
|
||||
SendPhoto(
|
||||
chatId,
|
||||
getAboutPic
|
||||
).caption(
|
||||
@ -64,6 +64,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
|$getMornyAboutLinksHTML"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML).replyToMessageId(replyTo)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
private def echoStickers (using command: InputCommand, event: Update): Unit = {
|
||||
@ -73,8 +74,8 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
else if (command.args.length == 2) command.args(1)
|
||||
else null
|
||||
} else if (command.args.length == 1) {
|
||||
if ((command.args(0) startsWith s"${Subs.STICKERS}.") || (command.args(0) startsWith s"${Subs.STICKERS}#")) {
|
||||
command.args(0) substring Subs.STICKERS.length+1
|
||||
if ((command.args(0) `startsWith` s"${Subs.STICKERS}.") || (command.args(0) `startsWith` s"${Subs.STICKERS}#")) {
|
||||
command.args(0) `substring` Subs.STICKERS.length+1
|
||||
} else null
|
||||
} else null
|
||||
if (mid == null) echo404
|
||||
@ -87,7 +88,7 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
echoSticker(_key, _file_id)
|
||||
else {
|
||||
try {
|
||||
val sticker = TelegramStickers getById mid
|
||||
val sticker = TelegramStickers `getById` mid
|
||||
echoSticker(sticker.getKey, sticker.getValue)
|
||||
} catch case _: NoSuchFieldException => {
|
||||
echo404
|
||||
@ -99,15 +100,15 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
val send_mid = SendMessage(send_chat, mid)
|
||||
val send_sticker = SendSticker(send_chat, file_id)
|
||||
if (send_replyTo != -1) send_mid.replyToMessageId(send_replyTo)
|
||||
val result_send_mid = coeur.account exec send_mid
|
||||
val result_send_mid = send_mid.unsafeExecute
|
||||
send_sticker.replyToMessageId(result_send_mid.message.messageId)
|
||||
coeur.account exec send_sticker
|
||||
send_sticker.unsafeExecute
|
||||
}
|
||||
|
||||
private[command] def echoVersion (using event: Update): Unit = {
|
||||
val versionDeltaHTML = MornySystem.VERSION_DELTA match {case Some(d) => s"-δ<code>${h(d)}</code>" case None => ""}
|
||||
val versionGitHTML = if (MornySystem.GIT_COMMIT nonEmpty) s"git $getVersionGitTagHTML" else ""
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
// language=html
|
||||
s"""version:
|
||||
@ -120,11 +121,12 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
|- <code>${h(formatDate(MornySystem.CODE_TIMESTAMP, 0))} [UTC]</code>
|
||||
|""".stripMargin
|
||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
private[command] def echoRuntime (using event: Update): Unit = {
|
||||
def sysprop (p: String): String = System.getProperty(p)
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
/* language=html */
|
||||
s"""system:
|
||||
@ -148,11 +150,12 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
|- [<code>${coeur.coeurStartTimestamp}</code>]"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
private def echoTasksStatus (using update: Update): Unit = {
|
||||
// if !coeur.trusted.isTrusted(update.message.from.id) then return;
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
// language=html
|
||||
s"""<b>Coeur Task Scheduler:</b>
|
||||
@ -161,26 +164,29 @@ class MornyInformation (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
| - <i>current runner status</i>: <code>${coeur.tasks.runnerState}</code>
|
||||
|""".stripMargin
|
||||
).parseMode(ParseMode.HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
private def echoEventStatistics (using update: Update): Unit = {
|
||||
coeur.externalContext >> { (reporter: MornyReport) =>
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
// language=html
|
||||
s"""<b>Event Statistics :</b>
|
||||
|in today
|
||||
|${reporter.EventStatistics.eventStatisticsHTML}""".stripMargin
|
||||
).parseMode(ParseMode.HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
} || {
|
||||
echo404
|
||||
}
|
||||
}
|
||||
|
||||
private def echo404 (using event: Update): Unit =
|
||||
coeur.account exec new SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
@ -8,13 +8,13 @@ import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendSticker
|
||||
|
||||
import scala.language.postfixOps
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class MornyManagers (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
object Exit extends ITelegramCommand {
|
||||
|
||||
@ -27,22 +27,24 @@ class MornyManagers (using coeur: MornyCoeur) {
|
||||
|
||||
val user = event.message.from
|
||||
|
||||
if (coeur.trusted isTrusted user.id) {
|
||||
if (coeur.trusted isTrust user) {
|
||||
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_EXIT
|
||||
).replyToMessageId(event.message.messageId)
|
||||
logger attention s"Morny exited by user ${user toLogTag}"
|
||||
.unsafeExecute
|
||||
logger `attention` s"Morny exited by user ${user toLogTag}"
|
||||
coeur.exit(0, user)
|
||||
|
||||
} else {
|
||||
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(event.message.messageId)
|
||||
logger attention s"403 exit caught from user ${user toLogTag}"
|
||||
.unsafeExecute
|
||||
logger `attention` s"403 exit caught from user ${user toLogTag}"
|
||||
coeur.externalContext.consume[MornyReport](_.unauthenticatedAction("/exit", user))
|
||||
|
||||
}
|
||||
@ -62,22 +64,24 @@ class MornyManagers (using coeur: MornyCoeur) {
|
||||
|
||||
val user = event.message.from
|
||||
|
||||
if (coeur.trusted isTrusted user.id) {
|
||||
if (coeur.trusted isTrust user) {
|
||||
|
||||
logger attention s"call save from command by ${user toLogTag}"
|
||||
logger `attention` s"call save from command by ${user toLogTag}"
|
||||
coeur.saveDataAll()
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_SAVED
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
} else {
|
||||
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(event.message.messageId)
|
||||
logger attention s"403 save caught from user ${user toLogTag}"
|
||||
.unsafeExecute
|
||||
logger `attention` s"403 save caught from user ${user toLogTag}"
|
||||
coeur.externalContext.consume[MornyReport](_.unauthenticatedAction("/save", user))
|
||||
|
||||
}
|
||||
|
@ -2,35 +2,38 @@ package cc.sukazyo.cono.morny.core.bot.event
|
||||
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener, InlineQueryUnit, MornyQueryManager}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.request.InlineQueryResult
|
||||
import com.pengrad.telegrambot.request.AnswerInlineQuery
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.collection.mutable.ListBuffer
|
||||
import scala.language.postfixOps
|
||||
import scala.reflect.ClassTag
|
||||
|
||||
class MornyOnInlineQuery (using queryManager: MornyQueryManager) (using coeur: MornyCoeur) extends EventListener {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override def onInlineQuery (using event: EventEnv): Unit = {
|
||||
import event.update
|
||||
|
||||
val results: List[InlineQueryUnit[_]] = queryManager query update
|
||||
val results: List[InlineQueryUnit[?]] = queryManager `query` update
|
||||
|
||||
var cacheTime = Int.MaxValue
|
||||
var isPersonal = InlineQueryUnit.defaults.IS_PERSONAL
|
||||
val resultAnswers = ListBuffer[InlineQueryResult[_]]()
|
||||
val resultAnswers = ListBuffer[InlineQueryResult[?]]()
|
||||
for (r <- results) {
|
||||
if (cacheTime > r.cacheTime) cacheTime = r.cacheTime
|
||||
if (r isPersonal) isPersonal = true
|
||||
resultAnswers += r.result
|
||||
}
|
||||
|
||||
if (results isEmpty) return;
|
||||
if (results isEmpty) return
|
||||
|
||||
coeur.account exec AnswerInlineQuery(
|
||||
update.inlineQuery.id, resultAnswers toArray:_*
|
||||
AnswerInlineQuery(
|
||||
update.inlineQuery.id,
|
||||
(resultAnswers toArray)*
|
||||
).cacheTime(cacheTime).isPersonal(isPersonal)
|
||||
.unsafeExecute
|
||||
|
||||
event.setEventOk
|
||||
|
||||
|
@ -14,21 +14,21 @@ class MornyOnTelegramCommand (using commandManager: MornyCommandManager) (using
|
||||
|
||||
def _isCommandMessage(message: Message): Boolean =
|
||||
if message.text eq null then false
|
||||
else if !(message.text startsWith "/") then false
|
||||
else if message.text startsWith "/ " then false
|
||||
else if !(message.text `startsWith` "/") then false
|
||||
else if message.text `startsWith` "/ " then false
|
||||
else true
|
||||
|
||||
if !_isCommandMessage(update.message) then return
|
||||
val inputCommand = InputCommand(update.message.text drop 1)
|
||||
givenCxt << inputCommand
|
||||
logger trace ":provided InputCommand for event"
|
||||
logger `trace` ":provided InputCommand for event"
|
||||
|
||||
if (!(inputCommand.command matches "^\\w+$"))
|
||||
logger debug "not command"
|
||||
if (!(inputCommand.command `matches` "^\\w+$"))
|
||||
logger `debug` "not command"
|
||||
else if ((inputCommand.target ne null) && (inputCommand.target != coeur.username))
|
||||
logger debug "not morny command"
|
||||
logger `debug` "not morny command"
|
||||
else
|
||||
logger debug "is command"
|
||||
logger `debug` "is command"
|
||||
if commandManager.execute(using inputCommand) then
|
||||
setEventOk
|
||||
|
||||
|
@ -20,7 +20,7 @@ class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventLi
|
||||
override def on (using event: EventEnv): Unit =
|
||||
event.update.sourceTime match
|
||||
case Some(timestamp) =>
|
||||
if coeur.config.eventIgnoreOutdated && (EpochMillis.fromEpochSeconds(timestamp) < coeur.coeurStartTimestamp) then
|
||||
if coeur.config.eventIgnoreOutdated && ((EpochMillis fromSeconds timestamp) < coeur.coeurStartTimestamp) then
|
||||
event.setEventCanceled
|
||||
case None =>
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
package cc.sukazyo.cono.morny.core.http.api
|
||||
|
||||
import cats.effect.IO
|
||||
import cc.sukazyo.cono.morny.core.Log.exceptionLog
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import org.http4s.{HttpRoutes, Response}
|
||||
|
||||
trait HttpService4Api {
|
||||
@ -13,7 +13,7 @@ trait HttpService4Api {
|
||||
response.setMornyInternalErrorHeader(
|
||||
e.getClass.getSimpleName,
|
||||
e.getMessage,
|
||||
exceptionLog(e)
|
||||
e.toLogString
|
||||
)
|
||||
def setMornyInternalErrorHeader (
|
||||
`Morny-Internal-Error-Type`: String,
|
||||
|
@ -2,6 +2,8 @@ package cc.sukazyo.cono.morny.core.http.api
|
||||
|
||||
trait MornyHttpServerContext {
|
||||
|
||||
infix def register4API (service: HttpService4Api): Unit
|
||||
|
||||
def register4API (service: HttpService4Api*): Unit
|
||||
|
||||
def start: HttpServer
|
||||
|
@ -3,7 +3,8 @@ package cc.sukazyo.cono.morny.core.http.internal
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.http.api.{HttpServer, HttpService4Api, MornyHttpServerContext}
|
||||
import cc.sukazyo.cono.morny.core.http.ServiceUI
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
@ -13,6 +14,9 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ
|
||||
|
||||
private lazy val service_ui = ServiceUI()
|
||||
|
||||
override infix def register4API (service: HttpService4Api): Unit =
|
||||
services_api += service
|
||||
|
||||
override def register4API (services: HttpService4Api*): Unit =
|
||||
services_api ++= services
|
||||
|
||||
@ -31,9 +35,9 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ
|
||||
|
||||
def errorHandler (t: Throwable, message: =>String): OptionT[IO, Unit] =
|
||||
OptionT.liftF(IO {
|
||||
logger error
|
||||
logger `error`
|
||||
s"""Unexpected exception occurred on Morny Http Server :
|
||||
|${exceptionLog(t)}""".stripMargin
|
||||
|${t.toLogString}""".stripMargin
|
||||
})
|
||||
val withErrorHandler = ErrorHandling.Recover.total(
|
||||
ErrorAction.log(
|
||||
@ -49,7 +53,7 @@ class MornyHttpServerContextImpl (using coeur: MornyCoeur) extends MornyHttpServ
|
||||
.resource
|
||||
val (_server, _shutdown_io) = server.allocated.unsafeRunSync() match
|
||||
case (_1, _2) => (_1, _2)
|
||||
logger notice s"Morny HTTP Server started at ${_server.baseUri}"
|
||||
logger `notice` s"Morny HTTP Server started at ${_server.baseUri}"
|
||||
|
||||
new HttpServer(using global):
|
||||
val server: Server = _server
|
||||
|
@ -5,9 +5,6 @@ import cc.sukazyo.cono.morny.util.CommonFormat.formatDate
|
||||
import cc.sukazyo.messiva.formatter.ILogFormatter
|
||||
import cc.sukazyo.messiva.log.Log
|
||||
|
||||
import java.time.{ZoneId, ZoneOffset}
|
||||
import java.util.TimeZone
|
||||
|
||||
class MornyFormatterConsole extends ILogFormatter {
|
||||
|
||||
override def format (log: Log): String =
|
||||
|
@ -8,7 +8,7 @@ class MornyLoggerBase extends Logger with IMornyLogLevelImpl {
|
||||
|
||||
def this (appends: IAppender*) =
|
||||
this()
|
||||
this.appends.addAll(java.util.List.of(appends:_*))
|
||||
this.appends.addAll(java.util.List.of(appends*))
|
||||
|
||||
override def notice (message: String): Unit =
|
||||
pushToAllAppender(Log(1, new Message(message), MornyLogLevels.NOTICE))
|
||||
|
@ -13,7 +13,7 @@ object MornyInformation {
|
||||
case None => ""
|
||||
case Some(commit) =>
|
||||
val g = StringBuilder()
|
||||
val cm = commit substring(0, 8)
|
||||
val cm = commit.substring(0, 8)
|
||||
val cp = MornySystem.currentCodePath
|
||||
if (cp == null) g ++= s"<code>$cm</code>"
|
||||
else g ++= s"<a href='$cp'>$cm</a>"
|
||||
|
@ -20,7 +20,7 @@ object TelegramImages {
|
||||
|
||||
@throws[AssetsException]
|
||||
private def read (): Unit = {
|
||||
Using ((MornyAssets.pack getResource assetsPath)read) { stream =>
|
||||
Using ((MornyAssets.pack `getResource` assetsPath)read) { stream =>
|
||||
try { this.cache = Some(stream.readAllBytes()) }
|
||||
catch case e: IOException => {
|
||||
throw AssetsException(e)
|
||||
|
@ -10,18 +10,20 @@ import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||
import cc.sukazyo.cono.morny.util.CommonEncrypt
|
||||
import cc.sukazyo.cono.morny.util.CommonEncrypt.*
|
||||
import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{PhotoSize, Update}
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{GetFile, SendDocument, SendMessage, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import java.io.IOException
|
||||
import java.net.{URLDecoder, URLEncoder}
|
||||
import java.util.Base64
|
||||
import scala.language.postfixOps
|
||||
|
||||
/** Provides Telegram Command __`/encrypt`__. */
|
||||
class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "encrypt"
|
||||
override val aliases: List[ICommandAlias] = ListedAlias("enc") :: Nil
|
||||
@ -33,7 +35,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
val args = command.args
|
||||
|
||||
// show a simple help page
|
||||
if ((args isEmpty) || ((args(0) equals "l") && (args.length == 1)))
|
||||
if ((args isEmpty) || ((args(0) `equals` "l") && (args.length == 1)))
|
||||
echoHelp(event.message.chat.id, event.message.messageId)
|
||||
return
|
||||
|
||||
@ -45,17 +47,18 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
// so the algorithm will be and must be in the 2nd arg.
|
||||
/** inner function: is input `arg` means mod-param ''uppercase'' */
|
||||
def _is_mod_u(arg: String): Boolean =
|
||||
if (arg equalsIgnoreCase "uppercase") return true
|
||||
if (arg equalsIgnoreCase "u") return true
|
||||
if (arg equalsIgnoreCase "upper") return true
|
||||
if (arg `equalsIgnoreCase` "uppercase") return true
|
||||
if (arg `equalsIgnoreCase` "u") return true
|
||||
if (arg `equalsIgnoreCase` "upper") return true
|
||||
false
|
||||
val mod_uppercase = if (args.length > 1) {
|
||||
if (args.length < 3 && _is_mod_u(args(1))) true
|
||||
else
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
} else false
|
||||
|
||||
@ -73,15 +76,16 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
val asByteArray: Array[Byte] = data
|
||||
/** inner class: the [[XEncryptable]] implementation of [[String]] data */
|
||||
case class XText (data: String) extends XEncryptable:
|
||||
val asByteArray: Array[Byte] = data getBytes CommonEncrypt.ENCRYPT_STANDARD_CHARSET
|
||||
val asByteArray: Array[Byte] = data.getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET)
|
||||
val input: XEncryptable =
|
||||
val _r = event.message.replyToMessage
|
||||
if ((_r ne null) && (_r.document ne null)) {
|
||||
try {XFile(
|
||||
coeur.account getFileContent (coeur.account exec GetFile(_r.document.fileId)).file,
|
||||
GetFile(_r.document.fileId).unsafeExecute
|
||||
.file.getContent,
|
||||
_r.document.fileName
|
||||
)} catch case e: IOException =>
|
||||
logger warn s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}"
|
||||
logger `warn` s"NetworkRequest error: TelegramFileAPI:\n\t${e.getMessage}"
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "NetworkRequest error: TelegramFileAPI"))
|
||||
return
|
||||
} else if ((_r ne null) && (_r.photo ne null)) {
|
||||
@ -96,26 +100,28 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
if (_photo_origin eq null) throw IllegalArgumentException("no photo from api.")
|
||||
import cc.sukazyo.cono.morny.util.UseRandom.rand_id
|
||||
XFile(
|
||||
coeur.account getFileContent (coeur.account exec GetFile(_photo_origin.fileId)).file,
|
||||
GetFile(_photo_origin.fileId).unsafeExecute
|
||||
.file.getContent,
|
||||
s"photo$rand_id.png"
|
||||
)
|
||||
} catch
|
||||
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}"
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "NetworkRequest error: TelegramFileAPI"))
|
||||
return
|
||||
case e: IllegalArgumentException =>
|
||||
logger warn s"FileProcess error: PhotoSize:\n\t${e.getMessage}"
|
||||
logger `warn` s"FileProcess error: PhotoSize:\n\t${e.getMessage}"
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "FileProcess error: PhotoSize"))
|
||||
return
|
||||
} else if ((_r ne null) && (_r.text ne null)) {
|
||||
XText(_r.text)
|
||||
} else {
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"<i><u>null</u></i>"
|
||||
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
}
|
||||
// END BLOCK: get input
|
||||
@ -141,14 +147,15 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
EXHash(if mod_uppercase then hashed toUpperCase else hashed)
|
||||
//noinspection UnitMethodIsParameterless
|
||||
def echo_unsupported: Unit =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
val result: EXHash|EXFile|EXText = args(0) match
|
||||
case "base64" | "b64" | "base64url" | "base64u" | "b64u" =>
|
||||
val _tool_b64 =
|
||||
if args(0) contains "u" then Base64.getUrlEncoder
|
||||
if args(0) `contains` "u" then Base64.getUrlEncoder
|
||||
else Base64.getEncoder
|
||||
genResult_encrypt(
|
||||
input,
|
||||
@ -157,7 +164,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
)
|
||||
case "base64decode" | "base64d" | "b64d" | "base64url-decode" | "base64ud" | "b64ud" =>
|
||||
val _tool_b64d =
|
||||
if args(0) contains "u" then Base64.getUrlDecoder
|
||||
if args(0) `contains` "u" then Base64.getUrlDecoder
|
||||
else Base64.getDecoder
|
||||
try { genResult_encrypt(
|
||||
input,
|
||||
@ -190,17 +197,19 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
// output
|
||||
result match
|
||||
case _file: EXFile =>
|
||||
coeur.account exec SendDocument(
|
||||
SendDocument(
|
||||
event.message.chat.id,
|
||||
_file.result
|
||||
).fileName(_file.resultName).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
case _text: EXTextLike =>
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
// language=html
|
||||
s"<pre><code>${h(_text.text)}</code></pre>"
|
||||
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
@ -231,7 +240,7 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
* </blockquote>
|
||||
*/
|
||||
private def echoHelp(chat: Long, replyTo: Int): Unit =
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
chat,
|
||||
s"""<b><u>base64</u></b>, b64
|
||||
|<b><u>base64url</u></b>, base64u, b64u
|
||||
@ -247,5 +256,6 @@ class Encryptor (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
|<b><i>uppercase</i></b>, upper, u <i>(sha1/sha256/sha512/md5 only)</i>"""
|
||||
.stripMargin
|
||||
).replyToMessageId(replyTo).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
@ -3,14 +3,16 @@ package cc.sukazyo.cono.morny.ip186
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class BotCommand (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private enum Subs (val cmd: String):
|
||||
case IP extends Subs("ip")
|
||||
@ -35,18 +37,20 @@ class BotCommand (using coeur: MornyCoeur) {
|
||||
if (command.args isEmpty)
|
||||
if event.message.replyToMessage eq null then null else event.message.replyToMessage.text
|
||||
else if (command.args.length > 1)
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"[Unavailable] Too much arguments."
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
else command.args(0)
|
||||
|
||||
if (target eq null)
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"[Unavailable] No ip defined."
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return;
|
||||
|
||||
|
||||
@ -58,20 +62,22 @@ class BotCommand (using coeur: MornyCoeur) {
|
||||
case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target)
|
||||
case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.command}")
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
s"""${h(response.url)}
|
||||
|<code>${h(response.body)}</code>"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
} catch case e: Exception =>
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message().chat().id(),
|
||||
s"""[Exception] in query:
|
||||
|<code>${h(e.getMessage)}</code>"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId())
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -28,7 +28,7 @@ object IP186QueryHandler {
|
||||
@throws[IOException]
|
||||
def query_whoisPretty (domain: String): IP186Response =
|
||||
val raw = query_whois(domain)
|
||||
IP186Response(raw.url, raw.body substring(0, (raw.body indexOf "<<<")+3))
|
||||
IP186Response(raw.url, raw.body `substring`(0, (raw.body `indexOf` "<<<")+3))
|
||||
|
||||
@throws[IOException]
|
||||
private def commonQuery (requestPath: Uri): IP186Response = {
|
||||
|
@ -4,7 +4,7 @@ import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.medication_timer.MedicationTimer.calcNextRoutineTimestamp
|
||||
import cc.sukazyo.cono.morny.util.schedule.RoutineTask
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import cc.sukazyo.cono.morny.util.CommonFormat
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||
import com.cronutils.builder.CronBuilder
|
||||
@ -13,12 +13,14 @@ import com.cronutils.model.time.ExecutionTime
|
||||
import com.pengrad.telegrambot.model.{Message, MessageEntity}
|
||||
import com.pengrad.telegrambot.request.{EditMessageText, SendMessage}
|
||||
import com.pengrad.telegrambot.response.SendResponse
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import java.time.{Instant, ZonedDateTime, ZoneOffset}
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
import scala.language.implicitConversions
|
||||
|
||||
class MedicationTimer (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private val NOTIFY_MESSAGE = "🍥⏲"
|
||||
private val DAEMON_THREAD_NAME_DEF = "MedicationTimer"
|
||||
@ -36,7 +38,7 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
||||
|
||||
def calcNextSendTime: EpochMillis =
|
||||
val next_time = calcNextRoutineTimestamp(System.currentTimeMillis, use_timeZone, notify_atHour)
|
||||
logger info s"medication timer will send next notify at ${CommonFormat.formatDate(next_time, use_timeZone.getTotalSeconds / 60 / 60)} with $use_timeZone [$next_time]"
|
||||
logger `info` s"medication timer will send next notify at ${CommonFormat.formatDate(next_time, use_timeZone.getTotalSeconds / 60 / 60)} with $use_timeZone [$next_time]"
|
||||
next_time
|
||||
|
||||
override def firstRoutineTimeMillis: EpochMillis =
|
||||
@ -47,24 +49,24 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
||||
|
||||
override def main: Unit = {
|
||||
sendNotification()
|
||||
logger info "medication notify sent."
|
||||
logger `info` "medication notify sent."
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
def start(): Unit =
|
||||
if ((notify_toChat == -1) || (notify_atHour isEmpty))
|
||||
logger notice "Medication Timer disabled : related param is not complete set"
|
||||
logger `notice` "Medication Timer disabled : related param is not complete set"
|
||||
return;
|
||||
coeur.tasks ++ scheduleTask
|
||||
logger notice "Medication Timer started."
|
||||
logger `notice` "Medication Timer started."
|
||||
|
||||
def stop(): Unit =
|
||||
coeur.tasks % scheduleTask
|
||||
logger notice "Medication Timer stopped."
|
||||
logger `notice` "Medication Timer stopped."
|
||||
|
||||
private def sendNotification(): Unit = {
|
||||
val sendResponse: SendResponse = coeur.account exec SendMessage(notify_toChat, NOTIFY_MESSAGE)
|
||||
val sendResponse: SendResponse = SendMessage(notify_toChat, NOTIFY_MESSAGE).unsafeExecute
|
||||
if sendResponse isOk then lastNotify_messageId = Some(sendResponse.message.messageId)
|
||||
else lastNotify_messageId = None
|
||||
}
|
||||
@ -76,11 +78,11 @@ class MedicationTimer (using coeur: MornyCoeur) {
|
||||
val entities = ArrayBuffer.empty[MessageEntity]
|
||||
if edited.entities ne null then entities ++= edited.entities
|
||||
entities += MessageEntity(MessageEntity.Type.italic, edited.text.length + "\n-- ".length, editTime.length)
|
||||
coeur.account exec EditMessageText(
|
||||
EditMessageText(
|
||||
notify_toChat,
|
||||
edited.messageId,
|
||||
edited.text + s"\n-- $editTime --"
|
||||
).entities(entities toArray:_*)
|
||||
).entities((entities toArray)*).unsafeExecute
|
||||
lastNotify_messageId = None
|
||||
true
|
||||
}
|
||||
@ -105,7 +107,7 @@ object MedicationTimer {
|
||||
}))
|
||||
.instance
|
||||
).nextExecution(
|
||||
ZonedDateTime ofInstant (Instant ofEpochMilli baseTimeMillis, zone.normalized)
|
||||
ZonedDateTime `ofInstant` (Instant `ofEpochMilli` baseTimeMillis, zone.normalized)
|
||||
).get.toInstant.toEpochMilli
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ class ModuleMedicationTimer extends MornyInternalModule {
|
||||
externalContext >> { (instance: MedicationTimer) =>
|
||||
eventManager register OnMedicationNotifyApply(using instance)
|
||||
} || {
|
||||
logger warn "There seems no Medication Timer instance is provided; skipped register events for it."
|
||||
logger `warn` "There seems no Medication Timer instance is provided; skipped register events for it."
|
||||
}
|
||||
|
||||
}
|
||||
@ -38,7 +38,7 @@ class ModuleMedicationTimer extends MornyInternalModule {
|
||||
externalContext >> { (instance: MedicationTimer) =>
|
||||
instance.start()
|
||||
} || {
|
||||
logger warn "There seems no Medication Timer instance is provided; skipped start it."
|
||||
logger `warn` "There seems no Medication Timer instance is provided; skipped start it."
|
||||
}
|
||||
|
||||
}
|
||||
@ -49,7 +49,7 @@ class ModuleMedicationTimer extends MornyInternalModule {
|
||||
externalContext >> { (instance: MedicationTimer) =>
|
||||
instance.stop()
|
||||
} || {
|
||||
logger warn "There seems no Medication Timer instance need to be stop."
|
||||
logger `warn` "There seems no Medication Timer instance need to be stop."
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import scala.language.postfixOps
|
||||
object MornyJrrp {
|
||||
|
||||
def jrrp_of_telegramUser (user: User, timestamp: EpochMillis): Double =
|
||||
jrrp_v_xmomi(user.id, EpochDays fromEpochMillis timestamp) * 100.0
|
||||
jrrp_v_xmomi(user.id, EpochDays fromMillis timestamp) * 100.0
|
||||
|
||||
private def jrrp_v_xmomi (identifier: Long, dayStamp: EpochDays): Double =
|
||||
import cc.sukazyo.cono.morny.util.CommonEncrypt.MD5
|
||||
|
@ -4,12 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramFormatter.*
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "jrrp"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
@ -26,11 +28,12 @@ class MornyOldJrrp (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
case _ => "..."
|
||||
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
// language=html
|
||||
f"${user.fullnameRefHTML} 在(utc的)今天的运气指数是———— <code>$jrrp%.2f%%</code> ${h(ending)}"
|
||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -3,25 +3,28 @@ package cc.sukazyo.cono.morny.morny_misc
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class Testing (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "test"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
|
||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
// language=html
|
||||
"<b>Just</b> a TEST command."
|
||||
).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
import sttp.client3.{HttpError, SttpClientException}
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class CommandNbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private val NBNHHSH_RESULT_HEAD_HTML =
|
||||
// language=html
|
||||
@ -33,52 +33,54 @@ class CommandNbnhhsh (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
else if (event.message.replyToMessage != null && event.message.replyToMessage.text != null)
|
||||
event.message.replyToMessage.text
|
||||
else
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_404
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return;
|
||||
|
||||
try {
|
||||
|
||||
val queryResp = NbnhhshQuery sendGuess queryTarget
|
||||
val queryResp = NbnhhshQuery `sendGuess` queryTarget
|
||||
|
||||
val message = StringBuilder(NBNHHSH_RESULT_HEAD_HTML)
|
||||
|
||||
logger trace s"**nbnhhsh got len=${queryResp.words.length}"
|
||||
logger `trace` s"**nbnhhsh got len=${queryResp.words.length}"
|
||||
for (_word <- queryResp.words) {
|
||||
logger trace s"**start for ${_word.name}"
|
||||
logger `trace` s"**start for ${_word.name}"
|
||||
val _use_trans = (_word.trans ne null) && (_word.trans nonEmpty)
|
||||
val _use_inputting = (_word.inputting ne null) && (_word.inputting nonEmpty)
|
||||
if (_use_trans || _use_inputting)
|
||||
message ++= s"\n\n<b>[[ ${h(_word.name)} ]]</b>"
|
||||
logger trace s"**used [${_word.name}]"
|
||||
logger `trace` s"**used [${_word.name}]"
|
||||
if (_use_trans) for (_trans <- _word.trans)
|
||||
message ++= s"\n* <i>${h(_trans)}</i>"
|
||||
logger trace s"**used [${_word.name}] used `${_trans}``"
|
||||
logger `trace` s"**used [${_word.name}] used `${_trans}``"
|
||||
if (_use_inputting)
|
||||
logger trace s"**used [${_word.name}] inputting"
|
||||
logger `trace` s"**used [${_word.name}] inputting"
|
||||
if (_use_trans)
|
||||
message += '\n'
|
||||
message ++= " maybe:"
|
||||
for (_inputting <- _word.inputting)
|
||||
logger trace s"**used [${_word.name}] used-i ${_inputting}"
|
||||
logger `trace` s"**used [${_word.name}] used-i ${_inputting}"
|
||||
message ++= s"\n` <i>${h(_inputting)}</i>"
|
||||
logger trace s"**done"
|
||||
logger `trace` s"**done"
|
||||
}
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
message toString
|
||||
).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
} catch case e: (HttpError[_] | SttpClientException) => {
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
s"""[Exception] in query:
|
||||
|${h(e.getMessage)}
|
||||
|""".stripMargin
|
||||
)
|
||||
).unsafeExecute
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ object NbnhhshQuery {
|
||||
|
||||
private val httpClient = OkHttpSyncBackend()
|
||||
|
||||
@throws[HttpError[_]|SttpClientException]
|
||||
@throws[HttpError[?]|SttpClientException]
|
||||
def sendGuess (text: String): GuessResult = {
|
||||
case class GuessRequest (text: String)
|
||||
val http = mornyBasicRequest
|
||||
|
@ -3,13 +3,15 @@ package cc.sukazyo.cono.morny.randomize_somthing
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.randomize_somthing.OnQuestionMarkReply.isAllMessageMark
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
import scala.util.boundary
|
||||
import scala.util.boundary.break
|
||||
|
||||
class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override def onMessage (using event: EventEnv): Unit = {
|
||||
import event.update
|
||||
@ -21,9 +23,10 @@ class OnQuestionMarkReply (using coeur: MornyCoeur) extends EventListener {
|
||||
if (1 over 8) chance_is false then return;
|
||||
if !isAllMessageMark(using update.message.text) then return;
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id, update.message.text
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
@ -40,7 +43,7 @@ object OnQuestionMarkReply {
|
||||
boundary[Boolean] {
|
||||
for (c <- text)
|
||||
if !(QUESTION_MARKS contains c) then
|
||||
boundary break false
|
||||
break(false)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
@ -2,13 +2,14 @@ package cc.sukazyo.cono.morny.randomize_somthing
|
||||
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.response.SendResponse
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class OnUserRandom (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
object RandomSelect extends EventListener {
|
||||
|
||||
@ -19,10 +20,10 @@ class OnUserRandom (using coeur: MornyCoeur) {
|
||||
import event.update
|
||||
|
||||
if update.message.text == null then return;
|
||||
if !(update.message.text startsWith "/") then return
|
||||
if !(update.message.text `startsWith` "/") then return
|
||||
|
||||
import cc.sukazyo.cono.morny.util.UseRandom.rand_half
|
||||
val query = update.message.text substring 1
|
||||
val query = update.message.text `substring` 1
|
||||
val result: String | Null = query match
|
||||
case USER_OR_QUERY(_con1, _con2) =>
|
||||
if rand_half then _con1 else _con2
|
||||
@ -34,10 +35,11 @@ class OnUserRandom (using coeur: MornyCoeur) {
|
||||
|
||||
if result == null then return;
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
result
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
@ -58,14 +60,15 @@ class OnUserRandom (using coeur: MornyCoeur) {
|
||||
var result: String|Null = null
|
||||
import cc.sukazyo.cono.morny.util.UseRandom.rand_half
|
||||
for (k <- keywords)
|
||||
if update.message.text endsWith k then
|
||||
if update.message.text `endsWith` k then
|
||||
result = if rand_half then "尊嘟" else "假嘟"
|
||||
if result == null then return;
|
||||
if result == null then return
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
result
|
||||
).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
|
@ -27,10 +27,10 @@ class Module extends MornyInternalModule {
|
||||
import cxt.*
|
||||
|
||||
externalContext >> { (instance: MornyReport) =>
|
||||
logger info "MornyReport will now collect your bot event statistics."
|
||||
logger `info` "MornyReport will now collect your bot event statistics."
|
||||
eventManager register instance.EventStatistics.EventInfoCatcher
|
||||
} || {
|
||||
logger warn "There seems no reporter instance is provided; skipped register events for it."
|
||||
logger `warn` "There seems no reporter instance is provided; skipped register events for it."
|
||||
}
|
||||
|
||||
}
|
||||
@ -40,7 +40,7 @@ class Module extends MornyInternalModule {
|
||||
externalContext >> { (instance: MornyReport) =>
|
||||
instance.start()
|
||||
} || {
|
||||
logger warn "There seems no reporter instance is provided; skipped start it."
|
||||
logger `warn` "There seems no reporter instance is provided; skipped start it."
|
||||
}
|
||||
}
|
||||
|
||||
@ -56,7 +56,7 @@ class Module extends MornyInternalModule {
|
||||
externalContext >> { (instance: MornyReport) =>
|
||||
instance.stop()
|
||||
} || {
|
||||
logger warn "There seems no reporter instance need to be stop."
|
||||
logger `warn` "There seems no reporter instance need to be stop."
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,19 +1,20 @@
|
||||
package cc.sukazyo.cono.morny.reporter
|
||||
|
||||
import cc.sukazyo.cono.morny.core.{MornyCoeur, MornyConfig}
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.data.MornyInformation.getVersionAllFullTagHTML
|
||||
import cc.sukazyo.cono.morny.util.statistics.{NumericStatistics, UniqueCounter}
|
||||
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.TelegramParseEscape.escapeHtml as h
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.DurationMillis
|
||||
import cc.sukazyo.cono.morny.util.schedule.CronTask
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Update.{sourceChat, sourceUser}
|
||||
import cc.sukazyo.cono.morny.util.CommonEncrypt.hashId
|
||||
import cc.sukazyo.cono.morny.util.ConvertByteHex.toHex
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import com.cronutils.builder.CronBuilder
|
||||
import com.cronutils.model.Cron
|
||||
import com.cronutils.model.definition.CronDefinitionBuilder
|
||||
@ -30,25 +31,25 @@ class MornyReport (using coeur: MornyCoeur) {
|
||||
|
||||
private val enabled = coeur.config.reportToChat != -1
|
||||
if !enabled then
|
||||
logger info "Morny Report is disabled : report chat is set to -1"
|
||||
logger `info` "Morny Report is disabled : report chat is set to -1"
|
||||
|
||||
private def executeReport[T <: BaseRequest[T, R], R<: BaseResponse] (report: T): Unit = {
|
||||
if !enabled then return
|
||||
try {
|
||||
coeur.account exec report
|
||||
report.unsafeExecute(using coeur.account)
|
||||
} catch case e: EventRuntimeException => {
|
||||
import EventRuntimeException.*
|
||||
e match
|
||||
case e: ActionFailed =>
|
||||
logger warn
|
||||
logger `warn`
|
||||
s"""cannot execute report to telegram:
|
||||
|${exceptionLog(e) indent 4}
|
||||
|${e.toLogString `indent` 4}
|
||||
| tg-api response:
|
||||
|${(e.response toString) indent 4}""".stripMargin
|
||||
|${(e.response toString) `indent` 4}""".stripMargin
|
||||
case e: ClientFailed =>
|
||||
logger error
|
||||
logger `error`
|
||||
s"""failed when report to telegram:
|
||||
|${exceptionLog(e.getCause) indent 4}
|
||||
|${e.getCause.toLogString `indent` 4}
|
||||
|""".stripMargin
|
||||
}
|
||||
}
|
||||
@ -69,7 +70,7 @@ class MornyReport (using coeur: MornyCoeur) {
|
||||
// language=html
|
||||
s"""<b>▌Coeur Unexpected Exception </b>
|
||||
|${if description ne null then h(description)+"\n" else ""}
|
||||
|<pre><code class="language-log">${h(exceptionLog(e))}</code></pre>$_tgErrFormat"""
|
||||
|<pre><code class="language-log">${h(e.toLogString)}</code></pre>$_tgErrFormat"""
|
||||
.stripMargin
|
||||
).parseMode(ParseMode HTML))
|
||||
}
|
||||
@ -120,9 +121,9 @@ class MornyReport (using coeur: MornyCoeur) {
|
||||
case e: (IllegalAccessException|IllegalArgumentException|NullPointerException) =>
|
||||
// language=html
|
||||
echo ++= s": <i>${h("<read-error>")}</i>"
|
||||
logger error
|
||||
logger `error`
|
||||
s"""error while reading config field ${field.getName}
|
||||
|${exceptionLog(e)}""".stripMargin
|
||||
|${e.toLogString}""".stripMargin
|
||||
exception(e, s"error while reading config field ${field.getName}")
|
||||
echo ++= "\n"
|
||||
}
|
||||
@ -224,14 +225,14 @@ class MornyReport (using coeur: MornyCoeur) {
|
||||
case State.OK(from) =>
|
||||
val timeUsed = EventTimeUsed(System.currentTimeMillis - event.timeStartup)
|
||||
givenCxt << timeUsed
|
||||
logger debug
|
||||
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
|
||||
logger `debug`
|
||||
s"""event done with CANCELED"
|
||||
| by $from""".stripMargin
|
||||
case null =>
|
||||
|
@ -5,14 +5,15 @@ import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
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.UniversalCommand
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private val TG_FORMAT = "^\\w+(@\\w+)?$"r
|
||||
|
||||
@ -22,7 +23,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
val text = update.message.text
|
||||
if text == null then return;
|
||||
|
||||
if (text startsWith "/") {
|
||||
if (text `startsWith` "/") {
|
||||
|
||||
// there has to be some special conditions for DP7
|
||||
// due to I have left DP7, I closed those special
|
||||
@ -33,7 +34,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
// these message, here to remember the old DP7.
|
||||
|
||||
val actions = UniversalCommand.Lossy(text)
|
||||
actions(0) = actions(0) substring 1
|
||||
actions(0) = actions(0) `substring` 1
|
||||
|
||||
actions(0)
|
||||
|
||||
@ -42,7 +43,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
case TG_FORMAT(_) =>
|
||||
return;
|
||||
// ignore Path link
|
||||
case x if x contains "/" => return;
|
||||
case x if x `contains` "/" => return;
|
||||
case _ =>
|
||||
|
||||
val isHardParse = actions(0) isBlank
|
||||
@ -52,7 +53,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
val hasObject = actions.length != hp_len(1)
|
||||
val v_object =
|
||||
if hasObject then
|
||||
actions slice(hp_len(1), actions.length) mkString " "
|
||||
actions `slice` (hp_len(1), actions.length) `mkString` " "
|
||||
else ""
|
||||
val origin = update.message
|
||||
val target =
|
||||
@ -60,7 +61,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
origin
|
||||
else update.message.replyToMessage
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
update.message.chat.id,
|
||||
"%s %s%s %s %s!".format(
|
||||
origin.sender_firstnameRefHTML,
|
||||
@ -71,6 +72,7 @@ class OnUserSlashAction (using coeur: MornyCoeur) extends EventListener {
|
||||
if hasObject then h(v_object+" ") else ""
|
||||
)
|
||||
).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
|
||||
.unsafeExecute
|
||||
event.setEventOk
|
||||
|
||||
}
|
||||
|
@ -25,4 +25,3 @@ class ModuleSocialShare extends MornyInternalModule {
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,11 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.InlineQueryUnit
|
||||
import cc.sukazyo.cono.morny.social_share.api.SocialContent.{SocialMedia, SocialMediaType, SocialMediaWithUrl}
|
||||
import cc.sukazyo.cono.morny.social_share.api.SocialContent.SocialMediaType.{Photo, Video}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
|
||||
import com.pengrad.telegrambot.model.request.*
|
||||
import com.pengrad.telegrambot.request.{SendMediaGroup, SendMessage}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
/** Model of social networks' status. for example twitter tweet or
|
||||
* weibo status.
|
||||
@ -44,18 +45,19 @@ case class SocialContent (
|
||||
case _ => orElse
|
||||
|
||||
def outputToTelegram (using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Unit = {
|
||||
given TelegramBot = coeur.account
|
||||
if medias isEmpty then
|
||||
coeur.account exec
|
||||
SendMessage(replyChat, text_html)
|
||||
.parseMode(ParseMode.HTML)
|
||||
.replyToMessageId(replyToMessage)
|
||||
SendMessage(replyChat, text_html)
|
||||
.parseMode(ParseMode.HTML)
|
||||
.replyToMessageId(replyToMessage)
|
||||
.unsafeExecute
|
||||
else
|
||||
val mediaGroup = medias.map(f => f.genTelegramInputMedia)
|
||||
mediaGroup.head.caption(text_html)
|
||||
mediaGroup.head.parseMode(ParseMode.HTML)
|
||||
coeur.account exec
|
||||
SendMediaGroup(replyChat, mediaGroup: _*)
|
||||
.replyToMessageId(replyToMessage)
|
||||
SendMediaGroup(replyChat, mediaGroup*)
|
||||
.replyToMessageId(replyToMessage)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
def genInlineQueryResults (using id_head: String, id_param: Any, name: String): List[InlineQueryUnit[?]] = {
|
||||
@ -99,13 +101,13 @@ object SocialContent {
|
||||
def genTelegramInputMedia: InputMedia[?]
|
||||
}
|
||||
case class SocialMediaWithUrl (url: String)(t: SocialMediaType) extends SocialMedia(t) {
|
||||
override def genTelegramInputMedia: InputMedia[_] =
|
||||
override def genTelegramInputMedia: InputMedia[?] =
|
||||
t match
|
||||
case Photo => InputMediaPhoto(url)
|
||||
case Video => InputMediaVideo(url)
|
||||
}
|
||||
case class SocialMediaWithBytesData (data: Array[Byte])(t: SocialMediaType) extends SocialMedia(t) {
|
||||
override def genTelegramInputMedia: InputMedia[_] =
|
||||
override def genTelegramInputMedia: InputMedia[?] =
|
||||
t match
|
||||
case Photo => InputMediaPhoto(data)
|
||||
case Video => InputMediaVideo(data)
|
||||
|
@ -5,11 +5,13 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
import cc.sukazyo.cono.morny.social_share.event.OnGetSocial
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendSticker
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "get"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
@ -19,10 +21,11 @@ class GetSocial (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||
|
||||
def do404 (): Unit =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers.ID_404
|
||||
).replyToMessageId(event.message.messageId())
|
||||
.unsafeExecute
|
||||
|
||||
val content =
|
||||
if command.args.length > 0 then
|
||||
|
@ -1,6 +1,6 @@
|
||||
package cc.sukazyo.cono.morny.social_share.event
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{EventEnv, EventListener}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
@ -8,11 +8,13 @@ import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||
import cc.sukazyo.cono.morny.social_share.api.{SocialTwitterParser, SocialWeiboParser}
|
||||
import cc.sukazyo.cono.morny.social_share.event.OnGetSocial.tryFetchSocial
|
||||
import cc.sukazyo.cono.morny.social_share.external.{twitter, weibo}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Message.textWithUrls
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import com.pengrad.telegrambot.model.Chat
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class OnGetSocial (using coeur: MornyCoeur) extends EventListener {
|
||||
|
||||
@ -80,36 +82,39 @@ object OnGetSocial {
|
||||
val api = FXApi.Fetch.status(Some(url.screenName), url.statusId)
|
||||
SocialTwitterParser.parseFXTweet(api).outputToTelegram
|
||||
} catch case e: (SttpClientException | ParsingFailure | DecodingFailure) =>
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
replyChat,
|
||||
TelegramStickers.ID_NETWORK_ERR
|
||||
).replyToMessageId(replyToMessage)
|
||||
logger error
|
||||
"Error on requesting FixTweet API\n" + exceptionLog(e)
|
||||
).replyToMessageId(replyToMessage).unsafeExecute(using coeur.account)
|
||||
logger `error`
|
||||
"Error on requesting FixTweet API\n" + e.toLogString
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "Error on requesting FixTweet API"))
|
||||
|
||||
def tryFetchSocialOfWeibo (url: weibo.StatusUrlInfo)(using replyChat: Long, replyToMessage: Int)(using coeur: MornyCoeur): Unit =
|
||||
import cc.sukazyo.cono.morny.social_share.external.weibo.MApi
|
||||
import io.circe.{DecodingFailure, ParsingFailure}
|
||||
import sttp.client3.{HttpError, SttpClientException}
|
||||
given TelegramBot = coeur.account
|
||||
try {
|
||||
val api = MApi.Fetch.statuses_show(url.id)
|
||||
SocialWeiboParser.parseMStatus(api).outputToTelegram
|
||||
} catch
|
||||
case e: HttpError[?] =>
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
replyChat,
|
||||
// language=html
|
||||
s"""Weibo Request Error <code>${e.statusCode}</code>
|
||||
|<pre><code>${e.body}</code></pre>""".stripMargin
|
||||
).replyToMessageId(replyToMessage).parseMode(ParseMode.HTML)
|
||||
.unsafeExecute
|
||||
case e: (SttpClientException | ParsingFailure | DecodingFailure) =>
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
replyChat,
|
||||
TelegramStickers.ID_NETWORK_ERR
|
||||
).replyToMessageId(replyToMessage)
|
||||
logger error
|
||||
"Error on requesting Weibo m.API\n" + exceptionLog(e)
|
||||
.unsafeExecute
|
||||
logger `error`
|
||||
"Error on requesting Weibo m.API\n" + e.toLogString
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e, "Error on requesting Weibo m.API"))
|
||||
|
||||
}
|
||||
|
@ -108,12 +108,12 @@ object BiliTool {
|
||||
*/
|
||||
def toBv (av: Long): String = {
|
||||
val _av = (av^V_CONV_XOR)+V_CONV_ADD
|
||||
val bv = Array(BV_TEMPLATE:_*)
|
||||
val bv = Array(BV_TEMPLATE*)
|
||||
for (i <- BV_TEMPLATE_FILTER.indices) {
|
||||
import Math.{floor, pow}
|
||||
bv(BV_TEMPLATE_FILTER(i)) = BV_TABLE( (floor(_av/(TABLE_INT**i)) % TABLE_INT) toInt )
|
||||
}
|
||||
String copyValueOf bv
|
||||
String `copyValueOf` bv
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ object BilibiliForms {
|
||||
IllegalArgumentException(s"not a b23.tv url: $uri")
|
||||
else if uri.pathSegments.segments.size < 1 then
|
||||
throw IllegalArgumentException(s"empty b23.tv url: $uri")
|
||||
else if uri.pathSegments.segments.head.v matches REGEX_BILI_ID.regex then
|
||||
else if uri.pathSegments.segments.head.v `matches` REGEX_BILI_ID.regex then
|
||||
throw IllegalArgumentException(s"is a b23 video link: $uri . (use parse_videoUrl instead)")
|
||||
|
||||
try {
|
||||
|
@ -33,7 +33,7 @@ object MApi {
|
||||
|
||||
private val httpClient = OkHttpSyncBackend()
|
||||
|
||||
@throws[HttpError[_]|SttpClientException|ParsingFailure|DecodingFailure]
|
||||
@throws[HttpError[?]|SttpClientException|ParsingFailure|DecodingFailure]
|
||||
def statuses_show (id: String): MApi[MStatus] =
|
||||
import sttp.client3.asString
|
||||
import MApi.CirceADTs.given
|
||||
@ -46,7 +46,7 @@ object MApi {
|
||||
.as[MApi[MStatus]]
|
||||
.toTry.get
|
||||
|
||||
@throws[HttpError[_] | SttpClientException | ParsingFailure | DecodingFailure]
|
||||
@throws[HttpError[?] | SttpClientException | ParsingFailure | DecodingFailure]
|
||||
def pic (picUrl: String): Array[Byte] =
|
||||
import sttp.client3.*
|
||||
import sttp.model.{MediaType, Uri}
|
||||
|
@ -1,10 +1,11 @@
|
||||
package cc.sukazyo.cono.morny.social_share.query
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.{exceptionLog, logger}
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{InlineQueryUnit, ITelegramQuery}
|
||||
import cc.sukazyo.cono.morny.reporter.MornyReport
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.NamingUtils.inlineQueryId
|
||||
import cc.sukazyo.cono.morny.util.UseThrowable.toLogString
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode}
|
||||
|
||||
@ -20,7 +21,7 @@ class ShareToolBilibili (using coeur: MornyCoeur) extends ITelegramQuery {
|
||||
private val LINK_PREFIX = "https://bilibili.com/video/"
|
||||
private val SHARE_FORMAT_HTML = "<a href='%s'>%s</a>"
|
||||
|
||||
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||
override def query (event: Update): List[InlineQueryUnit[?]] | Null = {
|
||||
|
||||
if (event.inlineQuery.query == null) return null
|
||||
if (event.inlineQuery.query isBlank) return null
|
||||
@ -36,7 +37,7 @@ class ShareToolBilibili (using coeur: MornyCoeur) extends ITelegramQuery {
|
||||
case _: IllegalArgumentException =>
|
||||
return null;
|
||||
case e: IllegalStateException =>
|
||||
logger error exceptionLog(e)
|
||||
logger `error` e.toLogString
|
||||
coeur.externalContext.consume[MornyReport](_.exception(e))
|
||||
return null;
|
||||
|
||||
|
@ -9,14 +9,14 @@ import com.pengrad.telegrambot.model.Update
|
||||
|
||||
class ShareToolSocialContent extends ITelegramQuery {
|
||||
|
||||
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||
override def query (event: Update): List[InlineQueryUnit[?]] | Null = {
|
||||
|
||||
val _queryRaw = event.inlineQuery.query
|
||||
val query =
|
||||
_queryRaw.trim match
|
||||
case _startsWithTag if _startsWithTag startsWith "get " =>
|
||||
case _startsWithTag if _startsWithTag `startsWith` "get " =>
|
||||
(_startsWithTag drop 4)trim
|
||||
case _endsWithTag if _endsWithTag endsWith " get" =>
|
||||
case _endsWithTag if _endsWithTag `endsWith` " get" =>
|
||||
(_endsWithTag dropRight 4)trim
|
||||
case _ => return null
|
||||
|
||||
|
@ -8,7 +8,6 @@ import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle
|
||||
|
||||
import scala.language.postfixOps
|
||||
import scala.util.matching.Regex
|
||||
|
||||
class ShareToolTwitter extends ITelegramQuery {
|
||||
|
||||
@ -17,7 +16,7 @@ class ShareToolTwitter extends ITelegramQuery {
|
||||
private val TITLE_FX = "[tweet] Share as Fix-Tweet"
|
||||
private val ID_PREFIX_FX = "[morny/share/twitter/fxtwi]"
|
||||
|
||||
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||
override def query (event: Update): List[InlineQueryUnit[?]] | Null = {
|
||||
|
||||
if (event.inlineQuery.query == null) return null
|
||||
|
||||
|
@ -4,7 +4,10 @@ import cats.effect.IO
|
||||
import cc.sukazyo.cono.morny.core.http.api.HttpService4Api
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.data.TelegramImages
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.File.getContent
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.execute
|
||||
import com.pengrad.telegrambot.request.GetFile
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
import org.http4s.{HttpRoutes, MediaType}
|
||||
import org.http4s.dsl.io.*
|
||||
import org.http4s.headers.`Content-Type`
|
||||
@ -17,10 +20,11 @@ class StickerService (using coeur: MornyCoeur) extends HttpService4Api {
|
||||
|
||||
case GET -> Root / "sticker" / "id" / id =>
|
||||
try {
|
||||
val response = coeur.account execute GetFile(id)
|
||||
val response = GetFile(id).execute(using coeur.account)
|
||||
if response.isOk then
|
||||
try {
|
||||
val file = coeur.account getFileContent response.file
|
||||
given TelegramBot = coeur.account
|
||||
val file = response.file.getContent
|
||||
Ok(file)
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
|
@ -12,7 +12,7 @@ class InlineRawText extends ITelegramQuery {
|
||||
private val ID_PREFIX = "[morny/r/text]"
|
||||
private val TITLE = "Raw Text"
|
||||
|
||||
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||
override def query (event: Update): List[InlineQueryUnit[?]] | Null = {
|
||||
|
||||
if (event.inlineQuery.query == null || (event.inlineQuery.query isBlank)) return null
|
||||
|
||||
|
@ -4,13 +4,15 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendSticker
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.language.postfixOps
|
||||
|
||||
class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "event_hack"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
@ -24,15 +26,17 @@ class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) exte
|
||||
val x_mode = if (command.args nonEmpty) command.args(0) else ""
|
||||
|
||||
def done_ok =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_WAITING
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
def done_forbiddenForAny =
|
||||
coeur.account exec SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_403
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
def doRegister (t: HackType): Unit =
|
||||
registerHack(
|
||||
@ -43,7 +47,7 @@ class CommandEventHack (using hacker: EventHacker)(using coeur: MornyCoeur) exte
|
||||
)
|
||||
x_mode match
|
||||
case "any" =>
|
||||
if (coeur.trusted isTrusted event.message.from.id)
|
||||
if (coeur.trusted isTrust event.message.from)
|
||||
doRegister(HackType ANY)
|
||||
done_ok
|
||||
else done_forbiddenForAny
|
||||
|
@ -2,15 +2,17 @@ package cc.sukazyo.cono.morny.tele_utils.event_hack
|
||||
|
||||
import cc.sukazyo.cono.morny.core.Log.logger
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
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 com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import scala.collection.mutable
|
||||
|
||||
class EventHacker (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
private case class Hacker (from_chat: Long, from_message: Long):
|
||||
override def toString: String = s"$from_chat/$from_message"
|
||||
@ -28,22 +30,23 @@ class EventHacker (using coeur: MornyCoeur) {
|
||||
case HackType.GROUP => s"{{$from_chat}}"
|
||||
case HackType.ANY => "[[]]"
|
||||
hackers += (record -> Hacker(from_chat, from_message))
|
||||
logger debug s"add hacker track $record"
|
||||
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))"
|
||||
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"
|
||||
logger `debug` s"hacked event by $x"
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramParseEscape.escapeHtml as h
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
x.from_chat,
|
||||
// language=html
|
||||
s"<pre><code class='language-json'>${h(GsonBuilder().setPrettyPrinting().create.toJson(update))}</code></pre>"
|
||||
).parseMode(ParseMode HTML).replyToMessageId(x.from_message toInt)
|
||||
.unsafeExecute
|
||||
true
|
||||
}
|
||||
|
||||
|
@ -4,14 +4,14 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.{InputCommand, Standardize}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.{execute, unsafeExecute}
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
|
||||
|
||||
import scala.language.postfixOps
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
class CommandGetUser (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "user"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
@ -23,46 +23,51 @@ class CommandGetUser (using coeur: MornyCoeur) extends ITelegramCommand {
|
||||
val args = command.args
|
||||
|
||||
if (args.length > 1)
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"[Unavailable] Too much arguments."
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
|
||||
val userId: Long =
|
||||
if (args nonEmpty) {
|
||||
try args(0) toLong
|
||||
catch case e: NumberFormatException =>
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
s"[Unavailable] ${e.getMessage}"
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
} else if (event.message.replyToMessage eq null) event.message.from.id
|
||||
else event.message.replyToMessage.from.id
|
||||
|
||||
val response = coeur.account execute GetChatMember(event.message.chat.id, userId)
|
||||
val response = GetChatMember(event.message.chat.id, userId).execute
|
||||
|
||||
if (response.chatMember eq null)
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"[Unavailable] user not found."
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return
|
||||
|
||||
val user = response.chatMember.user
|
||||
|
||||
if (user.id == Standardize.CHANNEL_SPEAKER_MAGIC_ID)
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
"<code>$__channel_identify</code>"
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
return;
|
||||
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
TelegramUserInformation getFormattedInformation user
|
||||
TelegramUserInformation.getFormattedInformation(user)
|
||||
).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -13,7 +13,7 @@ class InlineMyInformation extends ITelegramQuery {
|
||||
private val ID_PREFIX = "[morny/info/me]"
|
||||
private val TITLE = "My Account Information"
|
||||
|
||||
override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
|
||||
override def query (event: Update): List[InlineQueryUnit[?]] | Null = {
|
||||
|
||||
if !((event.inlineQuery.query eq null) || (event.inlineQuery.query isEmpty)) then return null
|
||||
|
||||
@ -21,7 +21,7 @@ class InlineMyInformation extends ITelegramQuery {
|
||||
InlineQueryUnit(InlineQueryResultArticle(
|
||||
inlineQueryId(ID_PREFIX), TITLE,
|
||||
new InputTextMessageContent(
|
||||
TelegramUserInformation getFormattedInformation event.inlineQuery.from
|
||||
TelegramUserInformation.getFormattedInformation(event.inlineQuery.from)
|
||||
).parseMode(ParseMode HTML)
|
||||
)).isPersonal(true).cacheTime(10)
|
||||
)
|
||||
|
@ -10,17 +10,17 @@ class BotEventUniMeowTrigger (using commands: UniMeowCommandManager) extends Eve
|
||||
import event.*
|
||||
|
||||
givenCxt >> { (input: InputCommand) =>
|
||||
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) {
|
||||
logger trace s"checking uni-meow $name"
|
||||
for ((name, command_instance) <- commands.getCommands) {
|
||||
logger `trace` s"checking uni-meow $name"
|
||||
if (name == input.command)
|
||||
logger trace "checked"
|
||||
logger `trace` "checked"
|
||||
command_instance.execute(using input, event.update)
|
||||
event.setEventOk
|
||||
}
|
||||
|
||||
} || { logger trace "not command (for uni-meow)" }
|
||||
} || { logger `trace` "not command (for uni-meow)" }
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,17 +1,9 @@
|
||||
package cc.sukazyo.cono.morny.uni_meow
|
||||
|
||||
import cc.sukazyo.cono.morny.core.bot.api.ISimpleCommand
|
||||
import cc.sukazyo.cono.morny.core.bot.api.MornyCommandManager.CommandMap
|
||||
import cc.sukazyo.cono.morny.core.bot.api.SimpleCommandManager
|
||||
|
||||
import scala.collection.mutable
|
||||
class UniMeowCommandManager extends SimpleCommandManager{
|
||||
|
||||
class UniMeowCommandManager {
|
||||
|
||||
private[uni_meow] val commands: CommandMap = mutable.SeqMap.empty
|
||||
def register [T <: ISimpleCommand] (commands: T*): Unit =
|
||||
for (i <- commands)
|
||||
this.commands += (i.name -> i)
|
||||
for (alias <- i.aliases)
|
||||
this.commands += (alias.name -> i)
|
||||
protected[uni_meow] def getCommands: this.commands.type = this.commands
|
||||
|
||||
}
|
||||
|
@ -3,12 +3,14 @@ package cc.sukazyo.cono.morny.uni_meow
|
||||
import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.*
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{MessageEntity, Update}
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
//noinspection NonAsciiCharacters
|
||||
class 创 (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
object Chuang extends ISimpleCommand {
|
||||
|
||||
@ -27,12 +29,13 @@ class 创 (using coeur: MornyCoeur) {
|
||||
return;
|
||||
|
||||
val chuangText = 创.chuangText(text)
|
||||
coeur.account exec SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
chuangText
|
||||
).entities(
|
||||
MessageEntity(MessageEntity.Type.pre, 0, chuangText.length)
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
|
||||
}
|
||||
|
||||
|
@ -4,10 +4,11 @@ import cc.sukazyo.cono.morny.core.MornyCoeur
|
||||
import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand, ITelegramCommand}
|
||||
import cc.sukazyo.cono.morny.data.TelegramStickers
|
||||
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.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.{Message, Update}
|
||||
import com.pengrad.telegrambot.model.request.ParseMode
|
||||
import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
import javax.swing.text.html.HTML
|
||||
import scala.annotation.unused
|
||||
@ -15,6 +16,7 @@ import scala.language.postfixOps
|
||||
|
||||
//noinspection NonAsciiCharacters
|
||||
class 喵呜 (using coeur: MornyCoeur) {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
object 抱抱 extends ISimpleCommand {
|
||||
override val name: String = "抱抱"
|
||||
@ -50,20 +52,22 @@ class 喵呜 (using coeur: MornyCoeur) {
|
||||
override val paramRule: String = ""
|
||||
override val description: String = "抽取一个神秘盒子"
|
||||
override def execute (using command: InputCommand, event: Update): Unit = {
|
||||
coeur.account exec new SendSticker(
|
||||
SendSticker(
|
||||
event.message.chat.id,
|
||||
TelegramStickers ID_PROGYNOVA
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
}
|
||||
|
||||
private def replyingSet (whileRec: String, whileNew: String)(using event: Update): Unit = {
|
||||
val isNew = event.message.replyToMessage == null
|
||||
val target = if (isNew) event.message else event.message.replyToMessage
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
if (isNew) whileNew else whileRec
|
||||
).replyToMessageId(target.messageId).parseMode(ParseMode HTML)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -5,12 +5,14 @@ import cc.sukazyo.cono.morny.core.bot.api.{ICommandAlias, ISimpleCommand}
|
||||
import cc.sukazyo.cono.morny.util.tgapi.InputCommand
|
||||
import cc.sukazyo.cono.morny.util.UseMath.over
|
||||
import cc.sukazyo.cono.morny.util.UseRandom.*
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Bot.exec
|
||||
import cc.sukazyo.cono.morny.util.tgapi.TelegramExtensions.Requests.unsafeExecute
|
||||
import com.pengrad.telegrambot.model.Update
|
||||
import com.pengrad.telegrambot.request.SendMessage
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
|
||||
//noinspection NonAsciiCharacters
|
||||
class 私わね (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
private given TelegramBot = coeur.account
|
||||
|
||||
override val name: String = "me"
|
||||
override val aliases: List[ICommandAlias] = Nil
|
||||
@ -19,10 +21,11 @@ class 私わね (using coeur: MornyCoeur) extends ISimpleCommand {
|
||||
|
||||
if ((1 over 521) chance_is true) {
|
||||
val text = "/打假"
|
||||
coeur.account exec new SendMessage(
|
||||
SendMessage(
|
||||
event.message.chat.id,
|
||||
text
|
||||
).replyToMessageId(event.message.messageId)
|
||||
.unsafeExecute
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ object CommonEncrypt {
|
||||
|
||||
private def hash (data: Bin)(using algorithm: String): Bin =
|
||||
try {
|
||||
MessageDigest.getInstance(algorithm) digest data
|
||||
MessageDigest.getInstance(algorithm) `digest` data
|
||||
} catch case n: NoSuchAlgorithmException =>
|
||||
throw IllegalStateException(n)
|
||||
|
||||
@ -47,7 +47,7 @@ object CommonEncrypt {
|
||||
*
|
||||
* $WhenString2Bin
|
||||
*/
|
||||
def MD5 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "md5")
|
||||
def MD5 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "md5")
|
||||
|
||||
/** the [[https://en.wikipedia.org/wiki/SHA-1 SHA-1]] hash value of input [[Bin]] `data`. */
|
||||
def SHA1 (data: Bin): Bin = hash(data)(using "sha1")
|
||||
@ -55,7 +55,7 @@ object CommonEncrypt {
|
||||
*
|
||||
* $WhenString2Bin
|
||||
*/
|
||||
def SHA1 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha1")
|
||||
def SHA1 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha1")
|
||||
|
||||
/** the [[https://en.wikipedia.org/wiki/SHA-2 SHA-2/256]] hash value of input [[Bin]] `data`. */
|
||||
def SHA256 (data: Bin): Bin = hash(data)(using "sha256")
|
||||
@ -63,7 +63,7 @@ object CommonEncrypt {
|
||||
*
|
||||
* $WhenString2Bin
|
||||
*/
|
||||
def SHA256 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha256")
|
||||
def SHA256 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha256")
|
||||
|
||||
/** the [[https://en.wikipedia.org/wiki/SHA-2 SHA-2/512]] hash value of input [[Bin]] `data`. */
|
||||
def SHA512 (data: Bin): Bin = hash(data)(using "sha512")
|
||||
@ -71,7 +71,7 @@ object CommonEncrypt {
|
||||
*
|
||||
* $WhenString2Bin
|
||||
*/
|
||||
def SHA512 (data: String): Bin = hash(data getBytes ENCRYPT_STANDARD_CHARSET)(using "sha512")
|
||||
def SHA512 (data: String): Bin = hash(data.getBytes(ENCRYPT_STANDARD_CHARSET))(using "sha512")
|
||||
|
||||
/** Try get the filename before it got encrypted.
|
||||
*
|
||||
@ -89,10 +89,10 @@ object CommonEncrypt {
|
||||
* @return the file fullname removed the base64 file extension.
|
||||
*/
|
||||
def lint_base64FileName (encrypted: String): String = encrypted match
|
||||
case i if i endsWith ".b64" => i dropRight ".b64".length
|
||||
case ix if ix endsWith ".b64.txt" => ix dropRight ".b64.txt".length
|
||||
case l if l endsWith ".base64" => l dropRight ".base64".length
|
||||
case lx if lx endsWith ".base64.txt" => lx dropRight ".base64.txt".length
|
||||
case i if i `endsWith` ".b64" => i dropRight ".b64".length
|
||||
case ix if ix `endsWith` ".b64.txt" => ix dropRight ".b64.txt".length
|
||||
case l if l `endsWith` ".base64" => l dropRight ".base64".length
|
||||
case lx if lx `endsWith` ".base64.txt" => lx dropRight ".base64.txt".length
|
||||
case u => u
|
||||
|
||||
/** Hash a [[Long]] id to [[Bin]] using [[MD5]] algorithm.
|
||||
|
@ -8,10 +8,14 @@ object EpochDateTime {
|
||||
/** The UNIX Epoch Time in milliseconds.
|
||||
*
|
||||
* aka. Milliseconds since 00:00:00 UTC on Thursday, 1 January 1970.
|
||||
*
|
||||
* @since 1.1.1
|
||||
*/
|
||||
type EpochMillis = Long
|
||||
object EpochMillis:
|
||||
/** convert a localtime with timezone to epoch milliseconds
|
||||
*
|
||||
* @since 1.1.1
|
||||
*
|
||||
* @param time the local time in that timezone, should be formatted
|
||||
* in [[DateTimeFormatter.ISO_DATE_TIME]]
|
||||
@ -22,7 +26,7 @@ object EpochDateTime {
|
||||
def apply (time: String, zone: String): EpochMillis = {
|
||||
val formatter = DateTimeFormatter.ISO_DATE_TIME
|
||||
val innerTime = LocalDateTime.parse(time, formatter)
|
||||
val instant = innerTime.toInstant(ZoneOffset of zone)
|
||||
val instant = innerTime.toInstant(ZoneOffset `of` zone)
|
||||
instant.toEpochMilli
|
||||
}
|
||||
def apply (time_zone: (String, String)): EpochMillis =
|
||||
@ -33,8 +37,10 @@ object EpochDateTime {
|
||||
*
|
||||
* Due to the missing accuracy, the converted EpochMillis will
|
||||
* be always in 0ms aligned.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
def fromEpochSeconds (epochSeconds: EpochSeconds): EpochMillis =
|
||||
infix def fromSeconds (epochSeconds: EpochSeconds): EpochMillis =
|
||||
epochSeconds.longValue * 1000L
|
||||
|
||||
/** The UNIX Epoch Time in seconds.
|
||||
@ -45,6 +51,8 @@ object EpochDateTime {
|
||||
*
|
||||
* Notice that, currently, it stores using [[Int]] (also the implementation
|
||||
* method of Telegram), which will only store times before 2038-01-19 03:14:07.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
type EpochSeconds = Int
|
||||
|
||||
@ -56,13 +64,20 @@ object EpochDateTime {
|
||||
*
|
||||
* Notice that, currently, it stores using [[Short]] (also the implementation
|
||||
* method of Telegram), which will only store times before 2059-09-18.
|
||||
*
|
||||
* @since 1.3.0
|
||||
*/
|
||||
type EpochDays = Short
|
||||
object EpochDays:
|
||||
def fromEpochMillis (epochMillis: EpochMillis): EpochDays =
|
||||
/** Convert a [[EpochMillis]] to [[EpochDays]]. Will be loss of precision.
|
||||
* @since 2.0.0
|
||||
*/
|
||||
infix def fromMillis (epochMillis: EpochMillis): EpochDays =
|
||||
(epochMillis / (1000*60*60*24)).toShort
|
||||
|
||||
/** Time duration/interval in milliseconds. */
|
||||
/** Time duration/interval in milliseconds.
|
||||
* @since 1.3.0
|
||||
*/
|
||||
type DurationMillis = Long
|
||||
|
||||
}
|
||||
|
@ -18,7 +18,7 @@ object FileUtils {
|
||||
Using (FileInputStream(path)) { stream =>
|
||||
len = stream.read(buffer)
|
||||
while (len != -1)
|
||||
algo update (buffer, 0, len)
|
||||
algo.update(buffer, 0, len)
|
||||
len = stream.read(buffer)
|
||||
}
|
||||
import ConvertByteHex.toHex
|
||||
|
@ -1,26 +1,17 @@
|
||||
package cc.sukazyo.cono.morny.util
|
||||
|
||||
import cc.sukazyo.cono.morny.util.GivenContext.{ContextNotGivenException, FolderClass, RequestItemClass}
|
||||
import cc.sukazyo.messiva.utils.StackUtils
|
||||
|
||||
import scala.annotation.targetName
|
||||
import scala.collection.mutable
|
||||
import scala.reflect.{classTag, ClassTag}
|
||||
import scala.util.boundary
|
||||
|
||||
object GivenContext {
|
||||
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,
|
||||
@ -69,7 +60,12 @@ object GivenContext {
|
||||
* } catch case e: ContextNotGivenException => // if any of the above val is not available, it will catch the exception
|
||||
* e.printStackTrace()
|
||||
* }}}
|
||||
*
|
||||
* TODO: Tests
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
//noinspection NoTargetNameAnnotationForOperatorLikeDefinition
|
||||
class GivenContext {
|
||||
|
||||
private type ImplicitsMap [T <: Any] = mutable.HashMap[Class[?], T]
|
||||
@ -77,7 +73,7 @@ class GivenContext {
|
||||
private val variables: ImplicitsMap[Any] = mutable.HashMap.empty
|
||||
private val variablesWithOwner: ImplicitsMap[ImplicitsMap[Any]] = mutable.HashMap.empty
|
||||
|
||||
def provide [T: ClassTag] (i: T): Unit =
|
||||
infix def provide [T: ClassTag] (i: T): Unit =
|
||||
variables += (classTag[T].runtimeClass -> i)
|
||||
def << [T: ClassTag] (is: (Class[T], T)): Unit =
|
||||
val (_, i) = is
|
||||
@ -91,7 +87,7 @@ class GivenContext {
|
||||
variables get t.clazz match
|
||||
case Some(i) => Right(i.asInstanceOf[T])
|
||||
case None => Left(ContextNotGivenException())
|
||||
def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
|
||||
infix def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
|
||||
this.use[T] match
|
||||
case Left(_) => ConsumeFailed[U]()
|
||||
case Right(i) => ConsumeSucceed[U](consumer(i))
|
||||
@ -101,7 +97,7 @@ class GivenContext {
|
||||
this.use[T].toTry.get
|
||||
def >>[T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
|
||||
this.use[T,U](consumer)
|
||||
def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
|
||||
infix def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
|
||||
this.use[T,Any](consume)
|
||||
|
||||
@targetName("ownedBy")
|
||||
@ -112,7 +108,7 @@ class GivenContext {
|
||||
|
||||
class OwnedContext [O: ClassTag] {
|
||||
|
||||
def provide [T: ClassTag] (i: T): Unit =
|
||||
infix def provide [T: ClassTag] (i: T): Unit =
|
||||
(variablesWithOwner getOrElseUpdate (classTag[O].runtimeClass, mutable.HashMap.empty))
|
||||
.addOne(classTag[T].runtimeClass -> i)
|
||||
def << [T: ClassTag] (is: (Class[T], T)): Unit =
|
||||
@ -129,7 +125,7 @@ class GivenContext {
|
||||
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] =
|
||||
infix def use [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
|
||||
use[T] match
|
||||
case Left(_) => ConsumeFailed[U]()
|
||||
case Right(i) => ConsumeSucceed[U](consumer(i))
|
||||
@ -139,7 +135,7 @@ class GivenContext {
|
||||
this.use[T].toTry.get
|
||||
def >> [T: ClassTag, U] (consumer: T => U): ConsumeResult[U] =
|
||||
this.use[T,U](consumer)
|
||||
def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
|
||||
infix def consume [T: ClassTag] (consume: T => Any): ConsumeResult[Any] =
|
||||
this.use[T,Any](consume)
|
||||
|
||||
}
|
||||
|
@ -5,13 +5,68 @@ object StringEnsure {
|
||||
|
||||
extension (str: String) {
|
||||
|
||||
def ensureSize(size: Int, paddingStr: Char = ' '): String = {
|
||||
/** Ensure the string have a length not smaller that the given length.
|
||||
*
|
||||
* If the length of the string is smaller than the given length, then the string will be padded
|
||||
* with the given padding character to the right, until the length of the string is not smaller
|
||||
* than the given length.
|
||||
*
|
||||
* For now, it does nothing if the length of the string is not smaller than the given length.
|
||||
*
|
||||
* @since 1.1.0
|
||||
*
|
||||
* @param size The minimum length that the string should have.
|
||||
* @param paddingStr The character that will be used to pad the string. Defaults to `' '` (a space).
|
||||
* @return A string that have been ensured to have a length not smaller than the given length.
|
||||
*/
|
||||
infix def ensureSize(size: Int, paddingStr: Char = ' '): String = {
|
||||
if (str.length < size) {
|
||||
val padding = paddingStr.toString * (size-str.length)
|
||||
str + padding
|
||||
} else str
|
||||
}
|
||||
|
||||
/** Replace the String with a given char, keeps the String length and characters only at the start
|
||||
* and end.
|
||||
*
|
||||
* This method can be used to de-sensitive some sensitive information, such as phone number, email,
|
||||
* etc. The default setting is to keep the first 2 characters and the last 4 characters, and replace
|
||||
* the rest with '*'.
|
||||
*
|
||||
* Notice that this method have un-defined behavior when the length of the String is less than
|
||||
* the character that will be kept, so change the character length that will be kept in your need.
|
||||
*
|
||||
* Examples:
|
||||
* {{{
|
||||
* scala> val someUserToken = "TOKEN_UV:V927c092FV$REFV[p':V<IE#*&@()U8eR)c"
|
||||
* val someUserToken: String = TOKEN_UV:V927c092FV$REFV[p':V<IE#*&@()U8eR)c
|
||||
*
|
||||
* scala> someUserToken.deSensitive()
|
||||
* val res1: String = TO**************************************eR)c
|
||||
*
|
||||
* scala> someUserToken.deSensitive(8, 4)
|
||||
* val res2: String = TOKEN_UV********************************eR)c
|
||||
*
|
||||
* scala> someUserToken.deSensitive(10, 4, '?')
|
||||
* val res3: String = TOKEN_UV:V??????????????????????????????eR)c
|
||||
*
|
||||
* scala> someUserToken.deSensitive(10, 4, '-')
|
||||
* val res4: String = TOKEN_UV:V------------------------------eR)c
|
||||
*
|
||||
* scala> "short".deSensitive(5, 5, '-')
|
||||
* val res5: String = shortshort
|
||||
* }}}
|
||||
*
|
||||
* @since 1.2.0
|
||||
*
|
||||
* @param keepStart The characters length that need to be kept at the start of the String. Defaults
|
||||
* to `2`.
|
||||
* @param keepEnd The characters length that need to be kept at the end of the String. Defaults to
|
||||
* `4`.
|
||||
* @param sensitive_cover The character that will be used to cover the sensitive information.
|
||||
* Defaults to `*`.
|
||||
* @return A string that have been de-sensitive.
|
||||
*/
|
||||
def deSensitive (keepStart: Int = 2, keepEnd: Int = 4, sensitive_cover: Char = '*'): String =
|
||||
(str take keepStart) + (sensitive_cover.toString*(str.length-keepStart-keepEnd)) + (str takeRight keepEnd)
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
package cc.sukazyo.cono.morny.util
|
||||
|
||||
import cc.sukazyo.cono.morny.core.MornySystem
|
||||
import sttp.client3.basicRequest
|
||||
import sttp.client3.{basicRequest, RequestT}
|
||||
import sttp.client3
|
||||
import sttp.model.Header
|
||||
|
||||
object SttpPublic {
|
||||
@ -17,13 +18,27 @@ object SttpPublic {
|
||||
|
||||
private val key = "User-Agent"
|
||||
|
||||
/** The default Morny User-Agent header.
|
||||
*
|
||||
* It follows the following template:
|
||||
* `MornyCoeur / <morny-version>`
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
val MORNY_CURRENT: Header = Header(key, s"MornyCoeur / ${MornySystem.VERSION}")
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val mornyBasicRequest =
|
||||
/** The basic request template for Morny's HTTP requests.
|
||||
*
|
||||
* It is a expansion of [[sttp.client3.basicRequest]] with the following features:
|
||||
* - A [[Headers.UserAgent.MORNY_CURRENT]] User-Agent header.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
val mornyBasicRequest: RequestT[client3.Empty, Either[String, String], Any] =
|
||||
basicRequest
|
||||
.header(Headers.UserAgent.MORNY_CURRENT, true)
|
||||
|
||||
|
@ -7,7 +7,7 @@ object UseMath {
|
||||
|
||||
extension (self: Int) {
|
||||
|
||||
def over (other: Int): Double = self.toDouble / other
|
||||
infix def over (other: Int): Double = self.toDouble / other
|
||||
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ object UseMath {
|
||||
}
|
||||
|
||||
extension (base: Int) {
|
||||
def percentageOf (another: Int): Int =
|
||||
infix def percentageOf (another: Int): Int =
|
||||
Math.round((another.toDouble/base)*100).toInt
|
||||
}
|
||||
|
||||
|
@ -10,7 +10,7 @@ import scala.util.Random
|
||||
object UseRandom {
|
||||
|
||||
class ChancePossibility[T <: Any] (val one: T) (using possibility: Double) {
|
||||
def nor[U] (another: U): T|U =
|
||||
infix def nor[U] (another: U): T|U =
|
||||
if Random.nextDouble < possibility then one else another
|
||||
}
|
||||
|
||||
@ -19,7 +19,7 @@ object UseRandom {
|
||||
|
||||
extension (num: Double) {
|
||||
|
||||
def chance_is[T <: Any] (one: T): ChancePossibility[T] =
|
||||
infix def chance_is[T <: Any] (one: T): ChancePossibility[T] =
|
||||
ChancePossibility(one)(using num)
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import cc.sukazyo.messiva.utils.StackUtils
|
||||
|
||||
import scala.reflect.{classTag, ClassTag}
|
||||
import scala.util.boundary
|
||||
import scala.util.boundary.break
|
||||
|
||||
object UseStacks {
|
||||
|
||||
@ -11,7 +12,7 @@ object UseStacks {
|
||||
boundary {
|
||||
for (stack <- StackUtils.getStackTrace(1)) {
|
||||
if (!stack.getClassName.startsWith(classTag[T].runtimeClass.getName))
|
||||
boundary break stack
|
||||
break(stack)
|
||||
}
|
||||
StackTraceElement("unknown", "unknown", "unknown", -1)
|
||||
}
|
||||
|
16
src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala
Normal file
16
src/main/scala/cc/sukazyo/cono/morny/util/UseThrowable.scala
Normal file
@ -0,0 +1,16 @@
|
||||
package cc.sukazyo.cono.morny.util
|
||||
|
||||
import java.io.{PrintWriter, StringWriter}
|
||||
|
||||
object UseThrowable {
|
||||
|
||||
extension (t: Throwable) {
|
||||
|
||||
def toLogString: String =
|
||||
val stackTrace = StringWriter()
|
||||
t `printStackTrace` PrintWriter(stackTrace)
|
||||
stackTrace toString
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -93,14 +93,14 @@ class Scheduler {
|
||||
case readyToRun: Task =>
|
||||
|
||||
runtimeStatus = State.RUNNING
|
||||
this setName readyToRun.name
|
||||
this `setName` readyToRun.name
|
||||
|
||||
try {
|
||||
readyToRun.main
|
||||
} catch case _: (Exception | Error) => {}
|
||||
|
||||
runtimeStatus = State.RUNNING_POST
|
||||
this setName s"${readyToRun.name}#post"
|
||||
this `setName` s"${readyToRun.name}#post"
|
||||
|
||||
// this if is used for check if post effect need to be
|
||||
// run. It is useless since the wait/notify changes.
|
||||
@ -117,7 +117,7 @@ class Scheduler {
|
||||
}
|
||||
|
||||
// currentRunning = null
|
||||
this setName runnerName
|
||||
this `setName` runnerName
|
||||
|
||||
case needToWaitMillis: EpochMillis =>
|
||||
runtimeStatus = State.WAITING
|
||||
@ -133,7 +133,7 @@ class Scheduler {
|
||||
}
|
||||
|
||||
}
|
||||
runtime setName runnerName
|
||||
runtime `setName` runnerName
|
||||
runtime.start()
|
||||
|
||||
/** Name of the scheduler runner.
|
||||
@ -179,7 +179,7 @@ class Scheduler {
|
||||
* succeed removed from task queue.
|
||||
*/
|
||||
def cancel (task: Task): Boolean =
|
||||
taskList synchronized:
|
||||
taskList.synchronized:
|
||||
try taskList remove task
|
||||
finally taskList.notifyAll()
|
||||
|
||||
@ -208,7 +208,7 @@ class Scheduler {
|
||||
* to make the scheduler avoid fails when machine fall asleep or some else conditions.
|
||||
*/
|
||||
def notifyIt(): Unit =
|
||||
taskList synchronized:
|
||||
taskList.synchronized:
|
||||
taskList.notifyAll()
|
||||
|
||||
/** Stop the scheduler's runner, no matter how much task is not run yet.
|
||||
|
@ -16,7 +16,7 @@ class InputCommand private (
|
||||
object InputCommand {
|
||||
|
||||
def apply (input: Array[String]): InputCommand = {
|
||||
val _ex = if input.nonEmpty then input(0) split ("@", 2) else Array.empty[String]
|
||||
val _ex = if input.nonEmpty then input(0).split("@", 2) else Array.empty[String]
|
||||
val _args = input drop 1
|
||||
new InputCommand(
|
||||
if _ex.length > 1 then _ex(1) else null,
|
||||
|
@ -1,12 +1,13 @@
|
||||
package cc.sukazyo.cono.morny.util.tgapi
|
||||
|
||||
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.{EpochMillis, EpochSeconds}
|
||||
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochSeconds
|
||||
import com.pengrad.telegrambot.TelegramBot
|
||||
import com.pengrad.telegrambot.model.*
|
||||
import com.pengrad.telegrambot.request.{BaseRequest, GetChatMember}
|
||||
import com.pengrad.telegrambot.response.BaseResponse
|
||||
|
||||
import java.io.IOException
|
||||
import scala.annotation.targetName
|
||||
|
||||
object TelegramExtensions {
|
||||
@ -39,7 +40,7 @@ object TelegramExtensions {
|
||||
@throws[EventRuntimeException]
|
||||
def exec [T <: BaseRequest[T, R], R <: BaseResponse] (request: BaseRequest[T, R], onError_message: String = ""): R = {
|
||||
try {
|
||||
val response = bot execute request
|
||||
val response = bot `execute` request
|
||||
if response isOk then return response
|
||||
throw EventRuntimeException.ActionFailed(
|
||||
if onError_message isEmpty then response.errorCode toString else onError_message,
|
||||
@ -229,11 +230,14 @@ object TelegramExtensions {
|
||||
* It needs to execute a request getting chat member, so it requires a live [[TelegramBot]] instance,
|
||||
* and the bot needs to be a member of this chat so that it can read the chat member.
|
||||
*
|
||||
* This method will only use the [[Chat]]'s [[Chat.id]], so it is safe to call on a [[LimboChat]].
|
||||
*
|
||||
* **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow.
|
||||
*
|
||||
* @see [[memberHasPermission]]
|
||||
*
|
||||
* @param user The user that want to check if they are a member of this chat.
|
||||
* @param user The user that want to check if they are a member of this chat.Only its [[User.id]] will
|
||||
* be used so it is safe using a [[LimboUser]].
|
||||
* @return [[true]] when the user is a member of this chat, [[false]] otherwise.
|
||||
*
|
||||
* @since 1.0.0
|
||||
@ -257,9 +261,14 @@ object TelegramExtensions {
|
||||
* | [[ChatMember.Status.left]] | -3 |
|
||||
* | [[ChatMember.Status.kicked]] | -5 |
|
||||
*
|
||||
* This method will only use the [[Chat]]'s [[Chat.id]], so it is safe to call on a [[LimboChat]].
|
||||
*
|
||||
* **Notice:** This method will execute a request to the Telegram Bot API, so it may be slow.
|
||||
*
|
||||
* @param user The user that wanted to check if they have the permission.
|
||||
* @since 1.0.0
|
||||
*
|
||||
* @param user The user that wanted to check if they have the permission. Only its [[User.id]] will
|
||||
* be used so it is safe using a [[LimboUser]].
|
||||
* @param permission The required permission level.
|
||||
* @param bot A live [[TelegramBot]] that will be used to execute the getChatMember request. It
|
||||
* should be a member of this chat so that can read the chat member for this method works.
|
||||
@ -288,8 +297,8 @@ object TelegramExtensions {
|
||||
case ChatMember.Status.kicked => KICKED
|
||||
def apply (chatMember: ChatMember): UserPermissionLevel = apply(chatMember.status)
|
||||
|
||||
import Bot.*
|
||||
val chatMember: ChatMember = (bot exec GetChatMember(chat.id, user.id)).chatMember
|
||||
import Requests.execute
|
||||
val chatMember: ChatMember = GetChatMember(chat.id, user.id).execute(using bot).chatMember
|
||||
if chatMember eq null then false
|
||||
else UserPermissionLevel(chatMember) >= UserPermissionLevel(permission)
|
||||
|
||||
@ -309,7 +318,65 @@ object TelegramExtensions {
|
||||
|
||||
}}
|
||||
|
||||
object File { extension (self: File) {
|
||||
|
||||
/** Alias of [[TelegramBot.getFileContent]] */
|
||||
@throws[IOException]
|
||||
def getContent (using bot: TelegramBot): Array[Byte] =
|
||||
bot.getFileContent(self)
|
||||
|
||||
/** Alias of [[TelegramBot.getFullFilePath]] */
|
||||
def getFullPath (using bot: TelegramBot): String =
|
||||
bot.getFullFilePath(self)
|
||||
|
||||
}}
|
||||
|
||||
object Requests { extension [T<:BaseRequest[T,R], R<:BaseResponse] (self: BaseRequest[T,R]) {
|
||||
/** Run this request with the given bot. Returns [[BaseResponse response]] if succeed, or throws
|
||||
* [[EventRuntimeException]] if run failed.
|
||||
*
|
||||
* This method is an alias for [[Bot.exec]]
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
@throws[EventRuntimeException]
|
||||
def unsafeExecute (using bot: TelegramBot): R =
|
||||
import Bot.exec
|
||||
bot.exec(self)
|
||||
/** Run this request with the given bot and returns no matter it succeed or not.
|
||||
*
|
||||
* Notice that if there's some client errors (like network error, etc.), this method will still
|
||||
* throws an Exception following the [[TelegramBot.execute]]'s behavior.
|
||||
*
|
||||
* This method is an alias for the native [[TelegramBot.execute]].
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
def execute (using bot: TelegramBot): R =
|
||||
bot.execute(self)
|
||||
}}
|
||||
|
||||
/** A [[User]] instance with only a [[User.id]] is defined.
|
||||
*
|
||||
* This is only for capabilities that some method need a [[User]] but only need its [[User.id]]. If
|
||||
* you don't have a [[User]] instance for some reason, you can use this instead.
|
||||
*
|
||||
* Many methods may crashes on this class for this is highly mutilated. Use this only the method declares
|
||||
* that using this is safe.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class LimboUser (id: Long) extends User(id)
|
||||
/** A [[Chat]] instance with only a [[Chat.id]] is defined.
|
||||
*
|
||||
* This is only for capabilities that some method need a [[Chat]] but only need its [[Chat.id]]. If
|
||||
* you don't have a [[Chat]] instance for some reason, you can use this instead.
|
||||
*
|
||||
* Many methods may crashes on this class for this is highly mutilated. Use this only the method declares
|
||||
* that using this is safe.
|
||||
*
|
||||
* @since 1.0.0
|
||||
*/
|
||||
class LimboChat (val _id: Long) extends Chat() {
|
||||
override val id: java.lang.Long = _id
|
||||
}
|
||||
|
@ -54,13 +54,13 @@ object TelegramParseEscape {
|
||||
case "br" =>
|
||||
result += TextNode("\n")
|
||||
case "tg-emoji" =>
|
||||
if elem.attributes.hasKey("emoji-id") then
|
||||
if elem.attributes `hasKey` "emoji-id" then
|
||||
result += produceChildNodes(elem)
|
||||
else
|
||||
result += TextNode(elem.text)
|
||||
case "img" =>
|
||||
if elem.attributes hasKey "alt" then
|
||||
result += TextNode(s"[${elem attr "alt"}]")
|
||||
if elem.attributes `hasKey` "alt" then
|
||||
result += TextNode(s"[${elem.attr("alt")}]")
|
||||
case _ =>
|
||||
for (i <- cleanupHtml(elem.childNodes.asScala.toSeq)) result += i
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ trait WatchDog (val isDaemonIt: Boolean = true) extends Thread {
|
||||
val overloadMillis: DurationMillis = tickSpeedMillis + (tickSpeedMillis/2)
|
||||
private var previousTickTimeMillis: Option[EpochMillis] = None
|
||||
|
||||
this setName threadName
|
||||
this setDaemon isDaemonIt
|
||||
this `setName` threadName
|
||||
this `setDaemon` isDaemonIt
|
||||
|
||||
this.start()
|
||||
|
||||
|
@ -8,6 +8,8 @@ import scala.io.StdIn
|
||||
@main def MornyCLI (): Unit = {
|
||||
|
||||
print("$ java -jar morny-coeur-\"+MornySystem.VERSION_FULL+\".jar ")
|
||||
ServerMain main UniversalCommand(StdIn readLine)
|
||||
ServerMain.main(
|
||||
UniversalCommand(StdIn readLine)
|
||||
)
|
||||
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ class MedicationTimerTest extends MornyTests with TableDrivenPropertyChecks {
|
||||
|
||||
forAll(examples) { (current, notifyAt, useTimezone, nextNotifyTime) =>
|
||||
val _curr = EpochMillis(current)
|
||||
val _tz = ZoneOffset of useTimezone
|
||||
val _tz = ZoneOffset `of` useTimezone
|
||||
val _next = EpochMillis(nextNotifyTime)
|
||||
|
||||
s"at time [$_curr], and need to be notify at hours ${notifyAt.mkString(",")} with $_tz :" - {
|
||||
|
@ -19,7 +19,7 @@ class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks {
|
||||
|
||||
forAll(examples) { (epochSeconds, epochMillis) =>
|
||||
s"EpochSeconds($epochSeconds) should be converted to EpochMillis($epochMillis)" in {
|
||||
(EpochMillis fromEpochSeconds epochSeconds) shouldEqual epochMillis
|
||||
(EpochMillis fromSeconds epochSeconds) shouldEqual epochMillis
|
||||
}
|
||||
}
|
||||
|
||||
@ -42,7 +42,7 @@ class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks {
|
||||
|
||||
forAll(examples) { (epochMillis, epochDays) =>
|
||||
s"EpochMillis($epochMillis) should be converted to EpochDays($epochDays)" in {
|
||||
(EpochDays fromEpochMillis epochMillis) shouldEqual epochDays
|
||||
(EpochDays fromMillis epochMillis) shouldEqual epochDays
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,7 +32,7 @@ class TaskBasicTest extends MornyTests {
|
||||
|
||||
"task can be sync executed by calling its main method." taggedAs Slow in {
|
||||
|
||||
Thread.currentThread setName "parent-thread"
|
||||
Thread.currentThread `setName` "parent-thread"
|
||||
val data = StringBuilder("")
|
||||
val task = Task("some-task", 0L, {
|
||||
Thread.sleep(100)
|
||||
|
Loading…
Reference in New Issue
Block a user