添加了 morny login/exit 的报告,*msg 现在支持了只包含发送 id 不包含发送体

- 为 MornyCoeur 添加了 whileExitReason (exit/getExitReason) 接口,用于安全退出时指定推出原因
- MornyConfig 添加了一个 Sensitive 注解用于标明字段值属于敏感字段不应该被打印
-
This commit is contained in:
A.C.Sukazyo Eyre 2022-11-11 16:37:24 +08:00
parent 01b4eea917
commit 3689962cb3
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
7 changed files with 123 additions and 12 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-alpha5 VERSION = 1.0.0-alpha6
USE_DELTA = false USE_DELTA = false
VERSION_DELTA = VERSION_DELTA =

View File

@ -60,6 +60,8 @@ public class MornyCoeur {
*/ */
public static final long coeurStartTimestamp = ServerMain.systemStartupTime; public static final long coeurStartTimestamp = ServerMain.systemStartupTime;
private Object whileExitReason = null;
private record LogInResult(TelegramBot account, String username, long userid) { } private record LogInResult(TelegramBot account, String username, long userid) { }
/** /**
@ -152,7 +154,6 @@ public class MornyCoeur {
* 用于退出时进行缓存的任务处理等进行安全退出 * 用于退出时进行缓存的任务处理等进行安全退出
*/ */
private void exitCleanup () { private void exitCleanup () {
logger.info("clean:save tracker data.");
MornyDaemons.stop(); MornyDaemons.stop();
if (config.commandLogoutClear) { if (config.commandLogoutClear) {
commandManager.automaticRemoveList(); commandManager.automaticRemoveList();
@ -304,4 +305,13 @@ public class MornyCoeur {
public static long getUserid () { return INSTANCE.userid; } 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;
}
} }

View File

@ -2,11 +2,20 @@ 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.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
public class MornyConfig { public class MornyConfig {
/**
* 表示一个字段的值属于敏感数据不应该被执行打印等操作
*/
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Target({ElementType.FIELD, ElementType.METHOD})
public @interface Sensitive {}
/* ======================================= * /* ======================================= *
* Config props Names Definition * * Config props Names Definition *
* ======================================= */ * ======================================= */
@ -37,7 +46,7 @@ public class MornyConfig {
* <p> * <p>
* 这个值必须设定 * 这个值必须设定
*/ */
@Nonnull public final String telegramBotKey; @Nonnull @Sensitive public final String telegramBotKey;
/** /**
* morny 所使用的 bot username. * morny 所使用的 bot username.
* <p> * <p>

View File

@ -213,7 +213,7 @@ public class MornyCommands {
).replyToMessageId(event.message().messageId()) ).replyToMessageId(event.message().messageId())
); );
logger.info("Morny exited by user " + TGToString.as(event.message().from()).toStringLogTag()); logger.info("Morny exited by user " + TGToString.as(event.message().from()).toStringLogTag());
System.exit(0); MornyCoeur.exit(0, event.message().from());
} else { } else {
MornyCoeur.extra().exec(new SendSticker( MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), event.message().chat().id(),

View File

@ -27,12 +27,12 @@ import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnCallMsgSend extends EventListener { 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 ( private record MessageToSend (
String message, @Nullable String message,
MessageEntity[] entities, @Nullable MessageEntity[] entities,
ParseMode parseMode, @Nullable ParseMode parseMode,
long targetId long targetId
) { } ) { }
@ -62,7 +62,7 @@ public class OnCallMsgSend extends EventListener {
// 发送体处理 // 发送体处理
if (update.message().replyToMessage() == null) return answer404(update); if (update.message().replyToMessage() == null) return answer404(update);
msgsendReqBody = parseRequest(update.message().replyToMessage()); 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)); SendResponse sendResponse = MornyCoeur.getAccount().execute(parseMessageToSend(msgsendReqBody));
if (!sendResponse.isOk()) { // 发送失败 if (!sendResponse.isOk()) { // 发送失败
@ -150,7 +150,8 @@ public class OnCallMsgSend extends EventListener {
).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML)); ).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()) parseMessageToSend(msgsendReqBody, update.message().chat().id()).replyToMessageId(update.message().messageId())
); );
if (!testSendResp.isOk()) { if (!testSendResp.isOk()) {

View File

@ -1,5 +1,7 @@
package cc.sukazyo.cono.morny.daemon; package cc.sukazyo.cono.morny.daemon;
import cc.sukazyo.cono.morny.MornyCoeur;
import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.cono.morny.Log.logger;
public class MornyDaemons { public class MornyDaemons {
@ -10,6 +12,7 @@ public class MornyDaemons {
logger.info("ALL Morny Daemons starting..."); logger.info("ALL Morny Daemons starting...");
// TrackerDataManager.init(); // TrackerDataManager.init();
medicationTimerInstance.start(); medicationTimerInstance.start();
MornyReport.onMornyLogIn();
logger.info("Morny Daemons started."); logger.info("Morny Daemons started.");
} }
@ -23,6 +26,7 @@ public class MornyDaemons {
// TrackerDataManager.trackingLock.lock(); // TrackerDataManager.trackingLock.lock();
try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); } try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); }
MornyReport.onMornyExit(MornyCoeur.getExitReason());
logger.info("ALL Morny Daemons STOPPED."); logger.info("ALL Morny Daemons STOPPED.");
} }

View File

@ -2,6 +2,7 @@ package cc.sukazyo.cono.morny.daemon;
import cc.sukazyo.cono.morny.Log; import cc.sukazyo.cono.morny.Log;
import cc.sukazyo.cono.morny.MornyCoeur; 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.event.EventRuntimeException;
import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString; import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
@ -14,6 +15,9 @@ import com.pengrad.telegrambot.response.BaseResponse;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; 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; import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class MornyReport { public class MornyReport {
@ -23,8 +27,10 @@ public class MornyReport {
try { try {
MornyCoeur.extra().exec(report); MornyCoeur.extra().exec(report);
} catch (EventRuntimeException.ActionFailed e) { } catch (EventRuntimeException.ActionFailed e) {
Log.logger.warn("cannot execute report to telegram:"); logger.warn("cannot execute report to telegram:");
Log.logger.warn(Log.exceptionLog(e)); 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)); ).parseMode(ParseMode.HTML));
} }
/**
* morny 登陆时的报告发送包含已登录的账号 id 以及启动配置
* @since 1.0.0-alpha6
*/
public static void onMornyLogIn () {
executeReport(new SendMessage(
MornyCoeur.config().reportToChat,
String.format("""
<b>Morny Logged in</b>
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("- <i><u>").append(field.getName()).append("</u></i> ");
try {
if (field.isAnnotationPresent(MornyConfig.Sensitive.class)) {
echo.append(": <i>sensitive_field</i>");
} else {
final Object fieldValue = field.get(config);
echo.append("= ");
if (fieldValue == null)
echo.append("null");
else echo.append("<code>").append(escapeHtml(fieldValue.toString())).append("</code>");
}
} catch (IllegalAccessException | IllegalArgumentException | NullPointerException e) {
echo.append(": <i>").append(escapeHtml("<read-error>")).append("</i>");
logger.error("error while reading config field " + field.getName());
logger.error(Log.exceptionLog(e));
exception(e, "error while reading config field " + field.getName());
}
echo.append("\n");
}
return echo.substring(0, echo.length()-1);
}
/**
* morny 关闭/登出时发送的报告.
* <p>
* 基于 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 = "<code>" + escapeHtml(causedBy.toString()) + "</code>";
}
executeReport(new SendMessage(
MornyCoeur.config().reportToChat,
String.format("""
<b>Morny Exited</b>
from user @%s
%s
""",
MornyCoeur.getUsername(),
causedBy == null ? "with UNKNOWN reason" : "\nby " + causedTag
)
).parseMode(ParseMode.HTML));
}
} }