diff --git a/gradle.properties b/gradle.properties index 9d43267..d8d160f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,7 +5,7 @@ MORNY_ARCHIVE_NAME = morny-coeur MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s -VERSION = 1.0.0-alpha5 +VERSION = 1.0.0-alpha6 USE_DELTA = false VERSION_DELTA = diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index d887b99..49f8655 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -60,6 +60,8 @@ public class MornyCoeur { */ public static final long coeurStartTimestamp = ServerMain.systemStartupTime; + private Object whileExitReason = null; + private record LogInResult(TelegramBot account, String username, long userid) { } /** @@ -152,7 +154,6 @@ public class MornyCoeur { * 用于退出时进行缓存的任务处理等进行安全退出 */ private void exitCleanup () { - logger.info("clean:save tracker data."); MornyDaemons.stop(); if (config.commandLogoutClear) { commandManager.automaticRemoveList(); @@ -304,4 +305,13 @@ public class MornyCoeur { public static long getUserid () { return INSTANCE.userid; } + public static void exit (int status, Object reason) { + INSTANCE.whileExitReason = reason; + System.exit(status); + } + + public static Object getExitReason () { + return INSTANCE.whileExitReason; + } + } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java b/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java index 0e02f9f..be61e15 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java @@ -2,11 +2,20 @@ package cc.sukazyo.cono.morny; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.lang.annotation.*; import java.util.HashSet; import java.util.Set; public class MornyConfig { + /** + * 表示一个字段的值属于敏感数据,不应该被执行打印等操作。 + */ + @Retention(RetentionPolicy.RUNTIME) + @Documented + @Target({ElementType.FIELD, ElementType.METHOD}) + public @interface Sensitive {} + /* ======================================= * * Config props Names Definition * * ======================================= */ @@ -37,7 +46,7 @@ public class MornyConfig { *
* 这个值必须设定。 */ - @Nonnull public final String telegramBotKey; + @Nonnull @Sensitive public final String telegramBotKey; /** * morny 所使用的 bot 的 username. *
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java
index 5925046..9ad3087 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java
@@ -213,7 +213,7 @@ public class MornyCommands {
).replyToMessageId(event.message().messageId())
);
logger.info("Morny exited by user " + TGToString.as(event.message().from()).toStringLogTag());
- System.exit(0);
+ MornyCoeur.exit(0, event.message().from());
} else {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java
index 0b362f9..b4eaaec 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java
@@ -27,12 +27,12 @@ import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnCallMsgSend extends EventListener {
- private static final Pattern REGEX_MSG_SENDREQ_DATA_HEAD = Pattern.compile("^\\*msg([\\d-]+)(\\*\\S+)?\\n([\\s\\S]+)$");
+ private static final Pattern REGEX_MSG_SENDREQ_DATA_HEAD = Pattern.compile("^\\*msg(-?\\d+)(\\*\\S+)?(?:\\n([\\s\\S]+))?$");
private record MessageToSend (
- String message,
- MessageEntity[] entities,
- ParseMode parseMode,
+ @Nullable String message,
+ @Nullable MessageEntity[] entities,
+ @Nullable ParseMode parseMode,
long targetId
) { }
@@ -62,7 +62,7 @@ public class OnCallMsgSend extends EventListener {
// 发送体处理
if (update.message().replyToMessage() == null) return answer404(update);
msgsendReqBody = parseRequest(update.message().replyToMessage());
- if (msgsendReqBody == null) return answer404(update);
+ if (msgsendReqBody == null || msgsendReqBody.message == null) return answer404(update);
// 执行发送任务
SendResponse sendResponse = MornyCoeur.getAccount().execute(parseMessageToSend(msgsendReqBody));
if (!sendResponse.isOk()) { // 发送失败
@@ -150,7 +150,8 @@ public class OnCallMsgSend extends EventListener {
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML));
}
// 发送文本测试
- SendResponse testSendResp = MornyCoeur.getAccount().execute(
+ if (msgsendReqBody.message == null) return true;
+ final SendResponse testSendResp = MornyCoeur.getAccount().execute(
parseMessageToSend(msgsendReqBody, update.message().chat().id()).replyToMessageId(update.message().messageId())
);
if (!testSendResp.isOk()) {
diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java
index d40ba47..710a07a 100644
--- a/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java
+++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java
@@ -1,5 +1,7 @@
package cc.sukazyo.cono.morny.daemon;
+import cc.sukazyo.cono.morny.MornyCoeur;
+
import static cc.sukazyo.cono.morny.Log.logger;
public class MornyDaemons {
@@ -10,6 +12,7 @@ public class MornyDaemons {
logger.info("ALL Morny Daemons starting...");
// TrackerDataManager.init();
medicationTimerInstance.start();
+ MornyReport.onMornyLogIn();
logger.info("Morny Daemons started.");
}
@@ -23,6 +26,7 @@ public class MornyDaemons {
// TrackerDataManager.trackingLock.lock();
try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); }
+ MornyReport.onMornyExit(MornyCoeur.getExitReason());
logger.info("ALL Morny Daemons STOPPED.");
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/MornyReport.java b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyReport.java
index a6d37e2..c79b223 100644
--- a/src/main/java/cc/sukazyo/cono/morny/daemon/MornyReport.java
+++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyReport.java
@@ -2,6 +2,7 @@ package cc.sukazyo.cono.morny.daemon;
import cc.sukazyo.cono.morny.Log;
import cc.sukazyo.cono.morny.MornyCoeur;
+import cc.sukazyo.cono.morny.MornyConfig;
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.google.gson.GsonBuilder;
@@ -14,6 +15,9 @@ import com.pengrad.telegrambot.response.BaseResponse;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
+import java.lang.reflect.Field;
+
+import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class MornyReport {
@@ -23,8 +27,10 @@ public class MornyReport {
try {
MornyCoeur.extra().exec(report);
} catch (EventRuntimeException.ActionFailed e) {
- Log.logger.warn("cannot execute report to telegram:");
- Log.logger.warn(Log.exceptionLog(e));
+ logger.warn("cannot execute report to telegram:");
+ logger.warn(Log.exceptionLog(e).indent(4));
+ logger.warn("tg-api response:");
+ logger.warn(e.getResponse().toString().indent(4));
}
}
@@ -64,4 +70,85 @@ public class MornyReport {
).parseMode(ParseMode.HTML));
}
+ /**
+ * morny 登陆时的报告发送,包含已登录的账号 id 以及启动配置。
+ * @since 1.0.0-alpha6
+ */
+ public static void onMornyLogIn () {
+ executeReport(new SendMessage(
+ MornyCoeur.config().reportToChat,
+ String.format("""
+ ▌Morny Logged in
+ as user @%s
+
+ as config fields:
+ %s
+ """,
+ MornyCoeur.getUsername(),
+ sectionConfigFields(MornyCoeur.config())
+ )
+ ).parseMode(ParseMode.HTML));
+ }
+
+ /**
+ * 返回一个 config 字段与值的列表,可以作为 telegram html 格式输出
+ * @since 1.0.0-alpha6
+ */
+ private static String sectionConfigFields (@Nonnull MornyConfig config) {
+ final StringBuilder echo = new StringBuilder();
+ for (Field field : config.getClass().getFields()) {
+ echo.append("- ").append(field.getName()).append(" ");
+ try {
+ if (field.isAnnotationPresent(MornyConfig.Sensitive.class)) {
+ echo.append(": sensitive_field");
+ } else {
+ final Object fieldValue = field.get(config);
+ echo.append("= ");
+ if (fieldValue == null)
+ echo.append("null");
+ else echo.append("").append(escapeHtml(fieldValue.toString())).append("
");
+ }
+ } catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
+ echo.append(": ").append(escapeHtml("
+ * 基于 java 的程序关闭钩子,因此仍然无法在意外宕机的情况下发送报告.
+ * @param causedBy
+ * 关闭的原因。
+ * 可以使用 {@link User Telegram 用户对象} 表示由一个用户执行了关闭,
+ * 传入其它数据将使用 {@code #toString} 输出其内容。
+ * 传入 {@link null} 则表示不表明原因。
+ */
+ static void onMornyExit (@Nullable Object causedBy) {
+ if (!MornyCoeur.available()) return;
+ String causedTag = null;
+ if (causedBy != null) {
+ if (causedBy instanceof User)
+ causedTag = TGToString.as((User)causedBy).fullnameRefHtml();
+ else
+ causedTag = "" + escapeHtml(causedBy.toString()) + "
";
+ }
+ executeReport(new SendMessage(
+ MornyCoeur.config().reportToChat,
+ String.format("""
+ ▌Morny Exited
+ from user @%s
+ %s
+ """,
+ MornyCoeur.getUsername(),
+ causedBy == null ? "with UNKNOWN reason" : "\nby " + causedTag
+ )
+ ).parseMode(ParseMode.HTML));
+ }
+
}