diff --git a/gradle.properties b/gradle.properties index cdee59b..9d43267 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-alpha4.2 +VERSION = 1.0.0-alpha5 USE_DELTA = false VERSION_DELTA = diff --git a/src/main/java/cc/sukazyo/cono/morny/Log.java b/src/main/java/cc/sukazyo/cono/morny/Log.java index b58c825..f4d1ea4 100644 --- a/src/main/java/cc/sukazyo/cono/morny/Log.java +++ b/src/main/java/cc/sukazyo/cono/morny/Log.java @@ -3,6 +3,9 @@ package cc.sukazyo.cono.morny; import cc.sukazyo.messiva.Logger; import cc.sukazyo.messiva.appender.ConsoleAppender; +import java.io.PrintWriter; +import java.io.StringWriter; + /** * Morny 的 log 管理器 */ @@ -15,4 +18,10 @@ public class Log { */ public static final Logger logger = new Logger(new ConsoleAppender()); + public static String exceptionLog (Exception e) { + final StringWriter stackTrace = new StringWriter(); + e.printStackTrace(new PrintWriter(stackTrace)); + return stackTrace.toString(); + } + } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index 452d0e4..d887b99 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -223,7 +223,7 @@ public class MornyCoeur { logger.info("Succeed login to @" + remote.username()); return new LogInResult(account, remote.username(), remote.id()); } catch (Exception e) { - e.printStackTrace(System.out); + logger.error(Log.exceptionLog(e)); logger.error("login failed."); } } @@ -239,6 +239,14 @@ public class MornyCoeur { logger.info("done all save action."); } + /** + * 检查 Coeur 是否已经完成初始化. + * @since 1.0.0-alpha5 + */ + public static boolean available() { + return INSTANCE != null; + } + /** * 获取登录成功后的 telegram bot 对象 * diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java b/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java index 004302c..0e02f9f 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyConfig.java @@ -88,6 +88,16 @@ public class MornyConfig { public final boolean commandLoginRefresh; public final boolean commandLogoutClear; + /* ======================================= * + * system: morny report * + * ======================================= */ + + /** + * 控制 Morny Coeur 系统的报告的报告对象. + * @since 1.0.0-alpha5 + */ + public final long reportToChat; + /* ======================================= * * function: dinner query tool * * ======================================= */ @@ -95,6 +105,10 @@ public class MornyConfig { @Nonnull public final Set dinnerTrustedReaders; public final long dinnerChatId; + /* ======================================= * + * End Configs | ConfigBuilder * + * ======================================= */ + public MornyConfig (@Nonnull Prototype prototype) throws CheckFailure { this.telegramBotApiServer = prototype.telegramBotApiServer; this.telegramBotApiServer4File = prototype.telegramBotApiServer4File; @@ -110,6 +124,7 @@ public class MornyConfig { this.commandLogoutClear = prototype.commandLogoutClear; this.dinnerTrustedReaders = prototype.dinnerTrustedReaders; this.dinnerChatId = prototype.dinnerChatId; + this.reportToChat = prototype.reportToChat; } public static class CheckFailure extends Exception { @@ -131,6 +146,7 @@ public class MornyConfig { public boolean commandLogoutClear = false; @Nonnull public Set dinnerTrustedReaders = new HashSet<>(); public long dinnerChatId = -1001707106392L; + public long reportToChat = -1001650050443L; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java index c00fc86..a4e2933 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java @@ -1,5 +1,6 @@ package cc.sukazyo.cono.morny; +import cc.sukazyo.cono.morny.daemon.MornyReport; import cc.sukazyo.cono.morny.util.BuildConfigField; import cc.sukazyo.cono.morny.util.FileUtils; @@ -135,7 +136,8 @@ public class MornySystem { } catch (IOException | URISyntaxException e) { return ""; } catch (NoSuchAlgorithmException e) { - e.printStackTrace(System.out); + Log.logger.error(Log.exceptionLog(e)); + MornyReport.exception(e, ""); return ""; } } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java index ae2b3bb..7a43fa0 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java @@ -27,6 +27,7 @@ public class MornyTrusted { */ public boolean isTrusted (long userId) { if (userId == instance.config.trustedMaster) return true; + if (instance.config.trustedChat == -1) return false; return MornyCoeur.extra().isUserInGroup(userId, instance.config.trustedChat, Status.administrator); } diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java index d07d498..6707185 100644 --- a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java +++ b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java @@ -49,6 +49,9 @@ public class ServerMain { * {@code --api-files} 单独设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot file api server * *
  • + * {@code --report-to} 设定 {@link cc.sukazyo.cono.morny.daemon.MornyReport} 的运行报告要发送到的 telegram 频道 + *
  • + *
  • * {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画。 * 与 {@code --only-hello} 参数不兼容 —— 会导致程序完全没有任何输出 *
  • @@ -158,6 +161,10 @@ public class ServerMain { config.telegramBotApiServer4File = args[i]; continue; } + case "--report-to" -> { + config.reportToChat = Long.parseLong(args[i]); + continue; + } } } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java index 18d0004..671dfd3 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java @@ -1,5 +1,7 @@ package cc.sukazyo.cono.morny.bot.api; +import cc.sukazyo.cono.morny.Log; +import cc.sukazyo.cono.morny.daemon.MornyReport; import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException; import com.google.gson.GsonBuilder; import com.pengrad.telegrambot.model.Update; @@ -32,27 +34,20 @@ public class EventListenerManager { if (exec.apply(x)) return; - } catch (EventRuntimeException e) { - - final StringBuilder errorMessage = new StringBuilder(); - errorMessage.append("Event runtime breaks: " + e.getMessage()).append('\n'); - errorMessage.append("at " + e.getStackTrace()[0].toString()).append('\n'); - errorMessage.append("at " + e.getStackTrace()[1].toString()).append('\n'); - errorMessage.append("at " + e.getStackTrace()[2].toString()).append('\n'); - errorMessage.append("at " + e.getStackTrace()[3].toString()).append('\n'); - if (e instanceof EventRuntimeException.ActionFailed) { - errorMessage.append(( - "\"telegram request track\": " + - new GsonBuilder().setPrettyPrinting().create().toJson(((EventRuntimeException.ActionFailed)e).getResponse()) - ).indent(4)).append('\n'); - } - - logger.error(errorMessage.toString()); - } catch (Exception e) { - logger.error("Event Error!"); - e.printStackTrace(System.out); + final StringBuilder errorMessage = new StringBuilder(); + errorMessage.append("Event throws unexpected exception:\n"); + errorMessage.append(Log.exceptionLog(e).indent(4)); + if (e instanceof EventRuntimeException.ActionFailed) { + errorMessage.append("\ntg-api action: response track: "); + errorMessage.append(new GsonBuilder().setPrettyPrinting().create().toJson( + ((EventRuntimeException.ActionFailed)e).getResponse() + ).indent(4)).append('\n'); + } + logger.error(errorMessage.toString()); + + MornyReport.exception(e, "on event running"); } } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Encryptor.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Encryptor.java index e770b9f..5ceb213 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Encryptor.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/Encryptor.java @@ -1,6 +1,7 @@ package cc.sukazyo.cono.morny.bot.command; import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.daemon.MornyReport; import cc.sukazyo.cono.morny.data.TelegramStickers; import cc.sukazyo.cono.morny.util.CommonConvert; import cc.sukazyo.cono.morny.util.CommonEncrypt; @@ -88,6 +89,7 @@ public class Encryptor implements ITelegramCommand { )).file()); } catch (IOException e) { logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage()); + MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI"); MornyCoeur.extra().exec(new SendSticker( event.message().chat().id(), TelegramStickers.ID_NETWORK_ERR @@ -110,6 +112,7 @@ public class Encryptor implements ITelegramCommand { )).file()); } catch (IOException e) { logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage()); + MornyReport.exception(e, "NetworkRequest error: TelegramFileAPI"); MornyCoeur.extra().exec(new SendSticker( event.message().chat().id(), TelegramStickers.ID_NETWORK_ERR 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 f5401fc..5925046 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 @@ -3,6 +3,7 @@ package cc.sukazyo.cono.morny.bot.command; import cc.sukazyo.cono.morny.BuildConfig; import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornySystem; +import cc.sukazyo.cono.morny.daemon.MornyReport; import cc.sukazyo.cono.morny.data.MornyJrrp; import cc.sukazyo.cono.morny.data.TelegramStickers; import cc.sukazyo.cono.morny.util.tgapi.InputCommand; @@ -220,6 +221,7 @@ public class MornyCommands { ).replyToMessageId(event.message().messageId()) ); logger.info("403 exited tag from user " + TGToString.as(event.message().from()).toStringLogTag()); + MornyReport.unauthenticatedAction("/exit", event.message().from()); } } @@ -375,6 +377,7 @@ public class MornyCommands { ).replyToMessageId(event.message().messageId()) ); logger.info("403 call save tag from user " + TGToString.as(event.message().from()).toStringLogTag()); + MornyReport.unauthenticatedAction("/save", event.message().from()); } } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java index d1bfb80..8039ce7 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java @@ -28,7 +28,7 @@ public class Testing implements ISimpleCommand { MornyCoeur.extra().exec(new SendMessage( event.message().chat().id(), - "Just a TEST command." + "Just a TEST command." ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java index 0c92c84..b776046 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java @@ -22,12 +22,13 @@ public class 私わね implements ISimpleCommand { public void execute (@Nonnull InputCommand command, @Nonnull Update event) { if (ThreadLocalRandom.current().nextInt(521) == 0) { // 可以接入未来的心情系统(如果有的话) - final String text = switch (ThreadLocalRandom.current().nextInt(11)) { - case 0,7,8,9,10 -> "才不是"; - case 1,2,3,6 -> "才不是!"; - case 4,5 -> "才不是.."; - default -> throw new IllegalStateException("Unexpected random value in 私わね command."); - }; +// final String text = switch (ThreadLocalRandom.current().nextInt(11)) { +// case 0,7,8,9,10 -> "才不是"; +// case 1,2,3,6 -> "才不是!"; +// case 4,5 -> "才不是.."; +// default -> throw new IllegalStateException("Unexpected random value in 私わね command."); +// }; + final String text = "/打假"; MornyCoeur.extra().exec(new SendMessage( event.message().chat().id(), text diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java b/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java index 813640b..5faba4a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java @@ -12,6 +12,7 @@ import com.pengrad.telegrambot.response.SendResponse; import java.util.ArrayList; import java.util.List; +import static cc.sukazyo.cono.morny.Log.exceptionLog; import static cc.sukazyo.cono.morny.Log.logger; public class MedicationTimer extends Thread { @@ -40,7 +41,8 @@ public class MedicationTimer extends Thread { logger.info("MedicationTimer was interrupted, will be exit now"); } catch (Exception e) { logger.error("Unexpected error occurred"); - e.printStackTrace(System.out); + logger.error(exceptionLog(e)); + MornyReport.exception(e); } } logger.info("MedicationTimer 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 new file mode 100644 index 0000000..a6d37e2 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyReport.java @@ -0,0 +1,67 @@ +package cc.sukazyo.cono.morny.daemon; + +import cc.sukazyo.cono.morny.Log; +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException; +import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString; +import com.google.gson.GsonBuilder; +import com.pengrad.telegrambot.model.User; +import com.pengrad.telegrambot.model.request.ParseMode; +import com.pengrad.telegrambot.request.BaseRequest; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.response.BaseResponse; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml; + +public class MornyReport { + + private static , R extends BaseResponse> void executeReport (@Nonnull T report) { + if (!MornyCoeur.available()) return; + try { + MornyCoeur.extra().exec(report); + } catch (EventRuntimeException.ActionFailed e) { + Log.logger.warn("cannot execute report to telegram:"); + Log.logger.warn(Log.exceptionLog(e)); + } + } + + public static void exception (@Nonnull Exception e, @Nullable String description) { + if (!MornyCoeur.available()) return; + executeReport(new SendMessage( + MornyCoeur.config().reportToChat, + String.format(""" + ▌Coeur Unexpected Exception + %s +
    %s
    %s + """, + description == null ? "" : escapeHtml(description)+"\n", + escapeHtml(Log.exceptionLog(e)), + e instanceof EventRuntimeException.ActionFailed ? (String.format( + "\n\ntg-api error:\n
    %s
    ", + new GsonBuilder().setPrettyPrinting().create().toJson(((EventRuntimeException.ActionFailed)e).getResponse())) + ) : "" + ) + ).parseMode(ParseMode.HTML)); + } + + public static void exception (@Nonnull Exception e) { exception(e, null); } + + public static void unauthenticatedAction (@Nonnull String action, @Nonnull User user) { + if (!MornyCoeur.available()) return; + executeReport(new SendMessage( + MornyCoeur.config().reportToChat, + String.format(""" + ▌User unauthenticated action + action: %s + by user %s + """, + escapeHtml(action), + TGToString.as(user).fullnameRefHtml() + ) + ).parseMode(ParseMode.HTML)); + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java b/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java index d0f8ab0..264a69a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java @@ -10,6 +10,7 @@ import java.util.HashMap; import java.util.TreeSet; import java.util.concurrent.locks.ReentrantLock; +import static cc.sukazyo.cono.morny.Log.exceptionLog; import static cc.sukazyo.cono.morny.Log.logger; public class TrackerDataManager { @@ -113,8 +114,10 @@ public class TrackerDataManager { )); } catch (Exception e) { - logger.error(String.format("exception in write tracker data: %d/%d/%d", chat, user, timestamp)); - e.printStackTrace(System.out); + final String message = String.format("exception in write tracker data: %d/%d/%d", chat, user, timestamp); + logger.error(message); + logger.error(exceptionLog(e)); + MornyReport.exception(e, message); } }