mirror of
https://github.com/Eyre-S/Coeur-Morny-Cono.git
synced 2024-11-23 11:37:38 +08:00
Compare commits
2 Commits
30c5ae71cc
...
0898d08fa7
Author | SHA1 | Date | |
---|---|---|---|
0898d08fa7 | |||
b617b3e059 |
@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur
|
|||||||
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono
|
||||||
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
|
||||||
|
|
||||||
VERSION = 1.1.0
|
VERSION = 1.1.1-alpha2
|
||||||
|
|
||||||
USE_DELTA = false
|
USE_DELTA = false
|
||||||
VERSION_DELTA =
|
VERSION_DELTA =
|
||||||
|
@ -42,6 +42,18 @@ class MornyCoeur (using val config: MornyConfig) {
|
|||||||
|
|
||||||
///<<< BLOCK END instance configure & startup stage 1
|
///<<< BLOCK END instance configure & startup stage 1
|
||||||
|
|
||||||
|
/** inner value: about why morny exit, used in [[daemon.MornyReport]]. */
|
||||||
|
private var whileExit_reason: Option[AnyRef] = None
|
||||||
|
/** About why morny exits. */
|
||||||
|
def exitReason: Option[AnyRef] = whileExit_reason
|
||||||
|
/** Stores when current Morny Coeur instance starts to initialize.
|
||||||
|
*
|
||||||
|
* The time is older than login but earlier than Coeur's daemons initialize.
|
||||||
|
*
|
||||||
|
* in milliseconds.
|
||||||
|
*/
|
||||||
|
val coeurStartTimestamp: Long = System.currentTimeMillis
|
||||||
|
|
||||||
/** [[TelegramBot]] account of this Morny */
|
/** [[TelegramBot]] account of this Morny */
|
||||||
val account: TelegramBot = __loginResult.account
|
val account: TelegramBot = __loginResult.account
|
||||||
/** [[account]]'s telegram username */
|
/** [[account]]'s telegram username */
|
||||||
@ -64,11 +76,6 @@ class MornyCoeur (using val config: MornyConfig) {
|
|||||||
//noinspection ScalaUnusedSymbol
|
//noinspection ScalaUnusedSymbol
|
||||||
val events: MornyEventListeners = MornyEventListeners(using eventManager)
|
val events: MornyEventListeners = MornyEventListeners(using eventManager)
|
||||||
|
|
||||||
/** inner value: about why morny exit, used in [[daemon.MornyReport]]. */
|
|
||||||
private var whileExit_reason: Option[AnyRef] = None
|
|
||||||
def exitReason: Option[AnyRef] = whileExit_reason
|
|
||||||
val coeurStartTimestamp: Long = ServerMain.systemStartupTime
|
|
||||||
|
|
||||||
///>>> BLOCK START instance configure & startup stage 2
|
///>>> BLOCK START instance configure & startup stage 2
|
||||||
|
|
||||||
daemons.start()
|
daemons.start()
|
||||||
|
@ -82,14 +82,15 @@ public class MornyConfig {
|
|||||||
* system: event ignore *
|
* system: event ignore *
|
||||||
* ======================================= */
|
* ======================================= */
|
||||||
|
|
||||||
public final boolean eventIgnoreOutdated;
|
|
||||||
/**
|
/**
|
||||||
* morny 的事件忽略前缀时间<br>
|
* Morny 是否忽略过期事件.
|
||||||
* <br>
|
* <br>
|
||||||
* {@link cc.sukazyo.cono.morny.bot.event.MornyOnUpdateTimestampOffsetLock}
|
* 过期事件即发生时间比 {@link MornyCoeur#coeurStartTimestamp()} 早的事件。
|
||||||
* 会根据这里定义的时间戳取消掉比此时间更早的事件链
|
* <br>
|
||||||
|
* 如果此项设置为 true, 则 {@link cc.sukazyo.cono.morny.bot.event.MornyOnUpdateTimestampOffsetLock}
|
||||||
|
* 会使事件时间比 {@link MornyCoeur#coeurStartTimestamp()} 早的事件跳过处理
|
||||||
*/
|
*/
|
||||||
public final long eventOutdatedTimestamp;
|
public final boolean eventIgnoreOutdated;
|
||||||
|
|
||||||
/* ======================================= *
|
/* ======================================= *
|
||||||
* system: command list automation *
|
* system: command list automation *
|
||||||
@ -138,8 +139,6 @@ public class MornyConfig {
|
|||||||
this.trustedMaster = prototype.trustedMaster;
|
this.trustedMaster = prototype.trustedMaster;
|
||||||
this.trustedChat = prototype.trustedChat;
|
this.trustedChat = prototype.trustedChat;
|
||||||
this.eventIgnoreOutdated = prototype.eventIgnoreOutdated;
|
this.eventIgnoreOutdated = prototype.eventIgnoreOutdated;
|
||||||
if (prototype.eventOutdatedTimestamp < 1) throw new CheckFailure.UnsetEventOutdatedTimestamp();
|
|
||||||
this.eventOutdatedTimestamp = prototype.eventOutdatedTimestamp;
|
|
||||||
this.commandLoginRefresh = prototype.commandLoginRefresh;
|
this.commandLoginRefresh = prototype.commandLoginRefresh;
|
||||||
this.commandLogoutClear = prototype.commandLogoutClear;
|
this.commandLogoutClear = prototype.commandLogoutClear;
|
||||||
this.dinnerTrustedReaders = prototype.dinnerTrustedReaders;
|
this.dinnerTrustedReaders = prototype.dinnerTrustedReaders;
|
||||||
@ -153,7 +152,6 @@ public class MornyConfig {
|
|||||||
|
|
||||||
public static class CheckFailure extends RuntimeException {
|
public static class CheckFailure extends RuntimeException {
|
||||||
public static class NullTelegramBotKey extends CheckFailure {}
|
public static class NullTelegramBotKey extends CheckFailure {}
|
||||||
public static class UnsetEventOutdatedTimestamp extends CheckFailure {}
|
|
||||||
public static class UnavailableTimeInMedicationNotifyAt extends CheckFailure {}
|
public static class UnavailableTimeInMedicationNotifyAt extends CheckFailure {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,7 +168,6 @@ public class MornyConfig {
|
|||||||
public long trustedMaster = -1L;
|
public long trustedMaster = -1L;
|
||||||
public long trustedChat = -1L;
|
public long trustedChat = -1L;
|
||||||
public boolean eventIgnoreOutdated = false;
|
public boolean eventIgnoreOutdated = false;
|
||||||
public long eventOutdatedTimestamp = -1;
|
|
||||||
public boolean commandLoginRefresh = false;
|
public boolean commandLoginRefresh = false;
|
||||||
public boolean commandLogoutClear = false;
|
public boolean commandLogoutClear = false;
|
||||||
@Nonnull public final Set<Long> dinnerTrustedReaders = new HashSet<>();
|
@Nonnull public final Set<Long> dinnerTrustedReaders = new HashSet<>();
|
||||||
|
@ -12,8 +12,6 @@ object ServerMain {
|
|||||||
|
|
||||||
private val THREAD_MORNY_INIT: String = "morny-init"
|
private val THREAD_MORNY_INIT: String = "morny-init"
|
||||||
|
|
||||||
val systemStartupTime: Long = System.currentTimeMillis()
|
|
||||||
|
|
||||||
def main (args: Array[String]): Unit = {
|
def main (args: Array[String]): Unit = {
|
||||||
|
|
||||||
val config = new MornyConfig.Prototype()
|
val config = new MornyConfig.Prototype()
|
||||||
@ -21,9 +19,8 @@ object ServerMain {
|
|||||||
var mode_echoHello = false
|
var mode_echoHello = false
|
||||||
var showHello = true
|
var showHello = true
|
||||||
|
|
||||||
config.eventOutdatedTimestamp = systemStartupTime
|
|
||||||
|
|
||||||
val unknownArgs = ArrayBuffer[String]()
|
val unknownArgs = ArrayBuffer[String]()
|
||||||
|
val deprecatedArgs = ArrayBuffer[(String, String)]()
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
while (i < args.length) {
|
while (i < args.length) {
|
||||||
@ -35,7 +32,11 @@ object ServerMain {
|
|||||||
case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true
|
case "--only-hello" | "-ho" | "-o" | "-hi" => mode_echoHello = true
|
||||||
case "--version" | "-v" => mode_echoVersion = true
|
case "--version" | "-v" => mode_echoVersion = true
|
||||||
|
|
||||||
case "--outdated-block" | "-ob" => config.eventIgnoreOutdated = true
|
// deprecated: use --outdated-ignore instead
|
||||||
|
case "--outdated-block" | "-ob" =>
|
||||||
|
config.eventIgnoreOutdated = true
|
||||||
|
deprecatedArgs += "--outdated-block" -> "--outdated-ignore"
|
||||||
|
case "--outdated-ignore" | "-oig" => config.eventIgnoreOutdated = true
|
||||||
|
|
||||||
case "--api" | "-a" => i+=1 ; config.telegramBotApiServer = args(i)
|
case "--api" | "-a" => i+=1 ; config.telegramBotApiServer = args(i)
|
||||||
case "--api-files" | "files-api" | "-af" => i+=1; config.telegramBotApiServer4File = args(i)
|
case "--api-files" | "files-api" | "-af" => i+=1; config.telegramBotApiServer4File = args(i)
|
||||||
@ -94,6 +95,10 @@ object ServerMain {
|
|||||||
s"""Can't understand arg to some meaning
|
s"""Can't understand arg to some meaning
|
||||||
| ${unknownArgs mkString "\n "}"""
|
| ${unknownArgs mkString "\n "}"""
|
||||||
.stripMargin
|
.stripMargin
|
||||||
|
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)
|
if (Log debug)
|
||||||
logger warn
|
logger warn
|
||||||
|
@ -7,7 +7,7 @@ import com.pengrad.telegrambot.model.Update
|
|||||||
class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
|
class MornyOnUpdateTimestampOffsetLock (using coeur: MornyCoeur) extends EventListener {
|
||||||
|
|
||||||
private def isOutdated (timestamp: Int): Boolean =
|
private def isOutdated (timestamp: Int): Boolean =
|
||||||
timestamp < (coeur.config.eventOutdatedTimestamp/1000)
|
coeur.config.eventIgnoreOutdated && (timestamp < (coeur.coeurStartTimestamp/1000))
|
||||||
|
|
||||||
override def onMessage (using update: Update): Boolean = isOutdated(update.message.date)
|
override def onMessage (using update: Update): Boolean = isOutdated(update.message.date)
|
||||||
override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date)
|
override def onEditedMessage (using update: Update): Boolean = isOutdated(update.editedMessage.date)
|
||||||
|
@ -87,7 +87,7 @@ class MedicationTimer (using coeur: MornyCoeur) extends Thread {
|
|||||||
object MedicationTimer {
|
object MedicationTimer {
|
||||||
|
|
||||||
@throws[IllegalArgumentException]
|
@throws[IllegalArgumentException]
|
||||||
private[daemon] def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = {
|
def calcNextRoutineTimestamp (baseTimeMillis: Long, zone: ZoneOffset, notifyAt: Set[Int]): Long = {
|
||||||
if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set")
|
if (notifyAt isEmpty) throw new IllegalArgumentException("notify time is not set")
|
||||||
var time = LocalDateTime.ofEpochSecond(
|
var time = LocalDateTime.ofEpochSecond(
|
||||||
baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt,
|
baseTimeMillis / 1000, ((baseTimeMillis % 1000) * 1000 * 1000) toInt,
|
||||||
|
@ -0,0 +1,29 @@
|
|||||||
|
package cc.sukazyo.cono.morny.util
|
||||||
|
|
||||||
|
import java.time.{LocalDateTime, ZoneOffset}
|
||||||
|
import java.time.format.DateTimeFormatter
|
||||||
|
|
||||||
|
object EpochDateTime {
|
||||||
|
|
||||||
|
type EpochMillis = Long
|
||||||
|
|
||||||
|
object EpochMillis:
|
||||||
|
/** convert a localtime with timezone to epoch milliseconds
|
||||||
|
*
|
||||||
|
* @param time the local time in that timezone, should be formatted
|
||||||
|
* in [[DateTimeFormatter.ISO_DATE_TIME]]
|
||||||
|
* @param zone timezone of the localtime.
|
||||||
|
* cannot be "UTC" or "GMT" (use "Z" instead)
|
||||||
|
* @return the epoch millisecond the local time means.
|
||||||
|
*/
|
||||||
|
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)
|
||||||
|
instant.toEpochMilli
|
||||||
|
}
|
||||||
|
def apply (time_zone: (String, String)): EpochMillis =
|
||||||
|
time_zone match
|
||||||
|
case (time, zone) => apply(time, zone)
|
||||||
|
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.event
|
package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.bot.event
|
||||||
|
|
||||||
import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply
|
import cc.sukazyo.cono.morny.bot.event.OnQuestionMarkReply
|
||||||
import cc.sukazyo.cono.morny.test.MornyTests
|
import cc.sukazyo.cono.morny.test.MornyTests
|
@ -0,0 +1,42 @@
|
|||||||
|
package cc.sukazyo.cono.morny.test.cc.sukazyo.cono.morny.daemon
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.daemon.MedicationTimer
|
||||||
|
import cc.sukazyo.cono.morny.test.MornyTests
|
||||||
|
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||||
|
import org.scalatest.prop.TableDrivenPropertyChecks
|
||||||
|
|
||||||
|
import java.time.ZoneOffset
|
||||||
|
|
||||||
|
class MedicationTimerTest extends MornyTests with TableDrivenPropertyChecks {
|
||||||
|
|
||||||
|
"on calculating next notify time :" - {
|
||||||
|
|
||||||
|
val examples = Table(
|
||||||
|
("current" , "notifyAt" , "useTimezone", "nextNotifyTime"),
|
||||||
|
(("2023-10-09T23:54:10.000","+8"), Set(1, 5, 19) , "+8" , ("2023-10-10T01:00:00.000","+8")),
|
||||||
|
(("2022-11-13T13:14:15.000","+2"), Set(7, 19, 21) , "+2" , ("2022-11-13T19:00:00.000","+2")),
|
||||||
|
(("2022-11-13T13:14:35.000","+8"), Set(7, 19, 21) , "+8" , ("2022-11-13T19:00:00" ,"+8")),
|
||||||
|
(("2022-11-13T13:14:35.174","+2"), Set(7, 19, 21) , "+2" , ("2022-11-13T19:00:00" ,"+2")),
|
||||||
|
(("1998-02-01T08:14:35.871","+8"), Set(7, 19, 21) , "+8" , ("1998-02-01T19:00:00" ,"+8")),
|
||||||
|
(("2022-11-13T00:00:00.000","-1"), Set(7, 19, 21) , "-1" , ("2022-11-13T07:00:00" ,"-1")),
|
||||||
|
(("2022-11-21T19:00:00.000","+0"), Set(7, 19, 21) , "+0" , ("2022-11-21T21:00:00" ,"+0")),
|
||||||
|
(("2022-12-31T21:00:00.000","+0"), Set(7, 19, 21) , "+0" , ("2023-01-01T07:00:00" ,"+0")),
|
||||||
|
(("2125-11-18T23:45:27.062","+0"), Set(7, 19, 21) , "+0" , ("2125-11-19T07:00:00" ,"+0"))
|
||||||
|
)
|
||||||
|
|
||||||
|
forAll (examples) { (current, notifyAt, useTimezone, nextNotifyTime) =>
|
||||||
|
val _curr = EpochMillis(current)
|
||||||
|
val _tz = ZoneOffset of useTimezone
|
||||||
|
val _next = EpochMillis(nextNotifyTime)
|
||||||
|
|
||||||
|
s"at time [$_curr], and need to be notify at hours ${notifyAt.mkString(",")} with $_tz :" - {
|
||||||
|
s"next notify should at time [$_curr]" in {
|
||||||
|
MedicationTimer.calcNextRoutineTimestamp(_curr, _tz, notifyAt) shouldEqual _next
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,31 @@
|
|||||||
|
package cc.sukazyo.cono.morny.test.utils
|
||||||
|
|
||||||
|
import cc.sukazyo.cono.morny.test.MornyTests
|
||||||
|
import cc.sukazyo.cono.morny.util.EpochDateTime.EpochMillis
|
||||||
|
import org.scalatest.prop.TableDrivenPropertyChecks
|
||||||
|
|
||||||
|
class EpochDateTimeTest extends MornyTests with TableDrivenPropertyChecks {
|
||||||
|
|
||||||
|
"while converting date-time string to time-millis : " - {
|
||||||
|
|
||||||
|
"while using ISO-Offset-Date-Time : " - {
|
||||||
|
|
||||||
|
val examples = Table(
|
||||||
|
("str" , "zone" , "millis"),
|
||||||
|
("2011-12-03T10:15:30" , "+01:00", 1322903730000L),
|
||||||
|
("2023-10-10T06:12:44.857", "Z" , 1696918364857L),
|
||||||
|
("2023-10-10T02:12:44.857", "-04:00", 1696918364857L),
|
||||||
|
("2023-10-10T14:12:44.857", "+8" , 1696918364857L),
|
||||||
|
("1938-04-24T22:13:20" , "Z" ,-1000000000000L),
|
||||||
|
)
|
||||||
|
|
||||||
|
forAll(examples) { (str, zone, millis) =>
|
||||||
|
s"$str should be epoch time millis $millis" in:
|
||||||
|
EpochMillis(str, zone) shouldEqual millis
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user