add -medc/medt/medtz params and configs for customize MedicationTimer status, fix millis/nanos conv problem again

- add configs for medication timer
  - medicationNotifyToChat
    - default is -1001729016815L(Annie medication notify chat)
    - add param --medication-notify-chat / -medc to set it, accept a long number.
  - medicationTimerUseTimezone
    - default is ZoneOffset.UTC(means +00:00)
    - add param --medication-notify-timezone / -medtz to set it, accept a number that means timezone hours
  - medicationNotifyAt
    - default is a empty Set
    - add param --medication-notify-times / -medt to set it, accept a hour(0~23) list seperated by ","
- now medication timer will exit if the notify-times is empty
 - changed the Test data-source for MedicationTimer#calcNextRoutineTimestamp
This commit is contained in:
A.C.Sukazyo Eyre 2022-11-18 17:49:39 +08:00
parent 2ffab30ef1
commit 8e28bbbce1
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
6 changed files with 67 additions and 19 deletions

View File

@ -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.0.0-RC2.1 VERSION = 1.0.0-RC3
USE_DELTA = false USE_DELTA = false
VERSION_DELTA = VERSION_DELTA =

View File

@ -3,6 +3,7 @@ package cc.sukazyo.cono.morny;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import java.lang.annotation.*; import java.lang.annotation.*;
import java.time.ZoneOffset;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
@ -114,6 +115,16 @@ public class MornyConfig {
@Nonnull public final Set<Long> dinnerTrustedReaders; @Nonnull public final Set<Long> dinnerTrustedReaders;
public final long dinnerChatId; public final long dinnerChatId;
/* ======================================= *
* function: medication timer *
* ======================================= */
public final long medicationNotifyToChat;
@Nonnull public final ZoneOffset medicationTimerUseTimezone;
@Nonnull public final Set<Integer> medicationNotifyAt;
/* ======================================= * /* ======================================= *
* End Configs | ConfigBuilder * * End Configs | ConfigBuilder *
* ======================================= */ * ======================================= */
@ -134,11 +145,16 @@ public class MornyConfig {
this.dinnerTrustedReaders = prototype.dinnerTrustedReaders; this.dinnerTrustedReaders = prototype.dinnerTrustedReaders;
this.dinnerChatId = prototype.dinnerChatId; this.dinnerChatId = prototype.dinnerChatId;
this.reportToChat = prototype.reportToChat; this.reportToChat = prototype.reportToChat;
this.medicationNotifyToChat = prototype.medicationNotifyToChat;
this.medicationTimerUseTimezone = prototype.medicationTimerUseTimezone;
prototype.medicationNotifyAt.forEach(i -> { if (i < 0 || i > 23) throw new CheckFailure.UnavailableTimeInMedicationNotifyAt(); });
this.medicationNotifyAt = prototype.medicationNotifyAt;
} }
public static class CheckFailure extends Exception { 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 UnsetEventOutdatedTimestamp extends CheckFailure {}
public static class UnavailableTimeInMedicationNotifyAt extends CheckFailure {}
} }
public static class Prototype { public static class Prototype {
@ -156,6 +172,9 @@ public class MornyConfig {
@Nonnull public Set<Long> dinnerTrustedReaders = new HashSet<>(); @Nonnull public Set<Long> dinnerTrustedReaders = new HashSet<>();
public long dinnerChatId = -1001707106392L; public long dinnerChatId = -1001707106392L;
public long reportToChat = -1001650050443L; public long reportToChat = -1001650050443L;
public long medicationNotifyToChat = -1001729016815L;
@Nonnull public ZoneOffset medicationTimerUseTimezone = ZoneOffset.UTC;
@Nonnull public Set<Integer> medicationNotifyAt = new HashSet<>();
} }

View File

@ -4,6 +4,8 @@ import cc.sukazyo.cono.morny.util.CommonFormat;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.time.ZoneOffset;
import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.cono.morny.Log.logger;
/** /**
@ -166,6 +168,22 @@ public class ServerMain {
config.reportToChat = Long.parseLong(args[i]); config.reportToChat = Long.parseLong(args[i]);
continue; continue;
} }
case "--medication-notify-chat", "-medc" -> {
i++;
config.medicationNotifyToChat = Long.parseLong(args[i]);
continue;
}
case "--medication-notify-timezone", "-medtz" -> {
i++;
config.medicationTimerUseTimezone = ZoneOffset.ofHours(Integer.parseInt(args[i]));
continue;
}
case "--medication-notify-times", "-medt" -> {
i++;
for (String u : args[i].split(","))
config.medicationNotifyAt.add(Integer.parseInt(u));
continue;
}
} }
} }

View File

@ -1,7 +1,7 @@
package cc.sukazyo.cono.morny.bot.event; package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.daemon.MedicationTimer;
import cc.sukazyo.cono.morny.daemon.MornyDaemons; import cc.sukazyo.cono.morny.daemon.MornyDaemons;
import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
@ -20,7 +20,7 @@ public class OnMedicationNotifyApply extends EventListener {
} }
private boolean editedMessageProcess (Message edited) { private boolean editedMessageProcess (Message edited) {
if (edited.chat().id() != MedicationTimer.NOTIFY_CHAT) return false; if (edited.chat().id() != MornyCoeur.config().medicationNotifyToChat) return false;
MornyDaemons.medicationTimerInstance.refreshNotificationWrite(edited); MornyDaemons.medicationTimerInstance.refreshNotificationWrite(edited);
return true; return true;
} }

View File

@ -20,15 +20,16 @@ import static cc.sukazyo.cono.morny.Log.logger;
public class MedicationTimer extends Thread { public class MedicationTimer extends Thread {
public static final ZoneOffset USE_TIME_ZONE = ZoneOffset.ofHours(8); private final ZoneOffset USE_TIME_ZONE = MornyCoeur.config().medicationTimerUseTimezone;
public static final Set<Integer> NOTIFY_AT_HOUR = Set.of(12); private final Set<Integer> NOTIFY_AT_HOUR = MornyCoeur.config().medicationNotifyAt;
public static final long NOTIFY_CHAT = -1001729016815L; private final long NOTIFY_CHAT = MornyCoeur.config().medicationNotifyToChat;
public static final String NOTIFY_MESSAGE = "\uD83C\uDF65⏲"; public static final String NOTIFY_MESSAGE = "\uD83C\uDF65⏲";
private static final String DAEMON_THREAD_NAME = "TIMER_Medication"; private static final String DAEMON_THREAD_NAME = "TIMER_Medication";
private static final long LAST_NOTIFY_ID_NULL = -1L; private static final long LAST_NOTIFY_ID_NULL = -1L;
private long lastNotify = LAST_NOTIFY_ID_NULL; private long lastNotify = LAST_NOTIFY_ID_NULL;
public static class NoNotifyTimeTag extends Throwable { private NoNotifyTimeTag(){} }
MedicationTimer () { MedicationTimer () {
super(DAEMON_THREAD_NAME); super(DAEMON_THREAD_NAME);
@ -44,6 +45,9 @@ public class MedicationTimer extends Thread {
} catch (InterruptedException e) { } catch (InterruptedException e) {
interrupt(); interrupt();
logger.info("MedicationTimer was interrupted, will be exit now"); logger.info("MedicationTimer was interrupted, will be exit now");
} catch (NoNotifyTimeTag ignored) {
logger.warn("Notify Time not Set and the MedicationTimer will not working!\nMedicationTimer will be exit now.");
interrupt();
} catch (Exception e) { } catch (Exception e) {
logger.error("Unexpected error occurred"); logger.error("Unexpected error occurred");
logger.error(exceptionLog(e)); logger.error(exceptionLog(e));
@ -74,9 +78,11 @@ public class MedicationTimer extends Thread {
lastNotify = LAST_NOTIFY_ID_NULL; lastNotify = LAST_NOTIFY_ID_NULL;
} }
public static long calcNextRoutineTimestamp (long baseTimeMillis, ZoneOffset useTimeZone, Set<Integer> atHours) { public static long calcNextRoutineTimestamp (long baseTimeMillis, ZoneOffset useTimeZone, Set<Integer> atHours)
throws NoNotifyTimeTag {
if (atHours.isEmpty()) throw new NoNotifyTimeTag();
LocalDateTime time = LocalDateTime.ofEpochSecond( LocalDateTime time = LocalDateTime.ofEpochSecond(
baseTimeMillis/1000, (int)baseTimeMillis%1000*1000, baseTimeMillis/1000, (int)(baseTimeMillis%1000)*1000*1000,
useTimeZone useTimeZone
).withMinute(0).withSecond(0).withNano(0); ).withMinute(0).withSecond(0).withNano(0);
do { do {
@ -85,7 +91,7 @@ public class MedicationTimer extends Thread {
return time.withMinute(0).withSecond(0).withNano(0).toInstant(useTimeZone).toEpochMilli(); return time.withMinute(0).withSecond(0).withNano(0).toInstant(useTimeZone).toEpochMilli();
} }
private void waitToNextRoutine () throws InterruptedException { private void waitToNextRoutine () throws InterruptedException, NoNotifyTimeTag {
sleep(calcNextRoutineTimestamp(System.currentTimeMillis(), USE_TIME_ZONE, NOTIFY_AT_HOUR) - System.currentTimeMillis()); sleep(calcNextRoutineTimestamp(System.currentTimeMillis(), USE_TIME_ZONE, NOTIFY_AT_HOUR) - System.currentTimeMillis());
} }

View File

@ -12,18 +12,23 @@ public class TestMedicationTimer {
@ParameterizedTest @ParameterizedTest
@CsvSource(textBlock = """ @CsvSource(textBlock = """
2022-11-13T13:14:35+08, +08, 2022-11-14T12:00:00+08 2022-11-13T13:14:35.000+08, +08, 2022-11-13T19:00:00+08
2022-11-13T13:14:35+02, +02, 2022-11-14T12:00:00+02 2022-11-13T13:14:35.174+02, +02, 2022-11-13T19:00:00+02
2022-11-13T08:14:35+08, +08, 2022-11-13T12:00:00+08 1998-02-01T08:14:35.871+08, +08, 1998-02-01T19:00:00+08
2022-11-13T00:14:35+08, +08, 2022-11-13T12:00:00+08 2022-11-13T00:00:00.000-01, -01, 2022-11-13T07:00:00-01
2022-11-13T12:00:00+00, +00, 2022-11-14T12:00:00+00 2022-11-21T19:00:00.000+00, +00, 2022-11-21T21:00:00+00
2022-12-31T21:00:00.000+00, +00, 2023-01-01T07:00:00+00
2125-11-18T23:45:27.062+00, +00, 2125-11-19T07:00:00+00
""") """)
void testCalcNextRoutineTimestamp (ZonedDateTime base, ZoneOffset zoneHour, ZonedDateTime expected) { void testCalcNextRoutineTimestamp (ZonedDateTime base, ZoneOffset zoneHour, ZonedDateTime expected)
final Set<Integer> at = Set.of(12); throws MedicationTimer.NoNotifyTimeTag {
final Set<Integer> at = Set.of(7, 19, 21);
System.out.println("base.toInstant().toEpochMilli() = " + base.toInstant().toEpochMilli());
Assertions.assertEquals( Assertions.assertEquals(
expected.toEpochSecond()*1000, expected.toInstant().toEpochMilli(),
MedicationTimer.calcNextRoutineTimestamp(base.toEpochSecond()*1000, zoneHour, at) MedicationTimer.calcNextRoutineTimestamp(base.toInstant().toEpochMilli(), zoneHour, at)
); );
System.out.println(" ok");
} }
} }