为error和exit/save 403 实现了 Morny Report,trustedChat= -1,typo 以及文案 fix

- 优化了 coeur 中 exception 的 stackTrace 的输出方式
  - 大幅度修改了 EventListenerManager 中报错的逻辑使其更加正常了许多
- 添加了 reportToChat 以及 --report-to 选项用在 MornyReport 中
- 为 coeur 中的错误报告添加了 MornyReport.exception
- 为 save/exit 两个需要权限的命令添加了 MornyReport.unauthenticatedAction
- 添加了一个方法可以检查 coeur(telegram_bot) 是否已完成初始化以存取 coeur 的内容
This commit is contained in:
A.C.Sukazyo Eyre 2022-11-10 23:06:52 +08:00
parent 618d777463
commit 01b4eea917
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
15 changed files with 149 additions and 32 deletions

View File

@ -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 =

View File

@ -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();
}
}

View File

@ -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 对象
*

View File

@ -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<Long> 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<Long> dinnerTrustedReaders = new HashSet<>();
public long dinnerChatId = -1001707106392L;
public long reportToChat = -1001650050443L;
}

View File

@ -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 "<non-jar-runtime>";
} catch (NoSuchAlgorithmException e) {
e.printStackTrace(System.out);
Log.logger.error(Log.exceptionLog(e));
MornyReport.exception(e, "<coeur-md5/calculation-error>");
return "<calculation-error>";
}
}

View File

@ -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);
}

View File

@ -49,6 +49,9 @@ public class ServerMain {
* {@code --api-files} 单独设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot file api server
* </li>
* <li>
* {@code --report-to} 设定 {@link cc.sukazyo.cono.morny.daemon.MornyReport} 的运行报告要发送到的 telegram 频道
* </li>
* <li>
* {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画
* {@code --only-hello} 参数不兼容 会导致程序完全没有任何输出
* </li>
@ -158,6 +161,10 @@ public class ServerMain {
config.telegramBotApiServer4File = args[i];
continue;
}
case "--report-to" -> {
config.reportToChat = Long.parseLong(args[i]);
continue;
}
}
}

View File

@ -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");
}
}

View File

@ -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

View File

@ -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());
}
}

View File

@ -28,7 +28,7 @@ public class Testing implements ISimpleCommand {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<b>Just<b/> a TEST command."
"<b>Just</b> a TEST command."
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}

View File

@ -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

View File

@ -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");

View File

@ -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 <T extends BaseRequest<T, R>, 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("""
<b>Coeur Unexpected Exception</b>
%s
<pre><code>%s</code></pre>%s
""",
description == null ? "" : escapeHtml(description)+"\n",
escapeHtml(Log.exceptionLog(e)),
e instanceof EventRuntimeException.ActionFailed ? (String.format(
"\n\ntg-api error:\n<pre><code>%s</code></pre>",
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("""
<b>User unauthenticated action</b>
action: %s
by user %s
""",
escapeHtml(action),
TGToString.as(user).fullnameRefHtml()
)
).parseMode(ParseMode.HTML));
}
}

View File

@ -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);
}
}