diff --git a/gradle.properties b/gradle.properties index 884ff8e..b211493 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ ## Core -VERSION = 0.6.5.1 +VERSION = 0.7.0.11 # dependencies diff --git a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java index efee5b7..b304e90 100644 --- a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java +++ b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java @@ -4,6 +4,6 @@ package cc.sukazyo.cono.morny; * the final field that will be updated by gradle automatically. */ public class GradleProjectConfigures { - public static final String VERSION = "0.6.5.1"; - public static final long COMPILE_TIMESTAMP = 1652771241057L; + public static final String VERSION = "0.7.0.11"; + public static final long COMPILE_TIMESTAMP = 1653443614307L; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index 84101b9..f010985 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -4,9 +4,11 @@ import cc.sukazyo.cono.morny.bot.api.OnUpdate; import cc.sukazyo.cono.morny.bot.command.MornyCommands; import cc.sukazyo.cono.morny.bot.event.EventListeners; import cc.sukazyo.cono.morny.bot.query.MornyQueries; -import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; +import cc.sukazyo.cono.morny.daemon.MornyDaemons; +import cc.sukazyo.cono.morny.daemon.TrackerDataManager; import cc.sukazyo.untitled.telegram.api.extra.ExtraAction; import com.pengrad.telegrambot.TelegramBot; +import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.request.GetMe; import javax.annotation.Nonnull; @@ -43,7 +45,13 @@ public class MornyCoeur { * 如果在登陆之前就定义了此字段,则登陆代码会验证登陆的 bot 的 username * 是否与定义的 username 符合。如果不符合则会报错。 */ - private final String username; + public final String username; + /** + * morny 的 bot 账户的 telegram id
+ *
+ * 这个字段将会在登陆成功后赋值为登录到的 bot 的 id。 + */ + public final long userid; /** * morny 的事件忽略前缀时间
*
@@ -59,7 +67,7 @@ public class MornyCoeur { public static final long DINNER_CHAT_ID = -1001707106392L; - private record LogInResult(TelegramBot account, String username) { } + private record LogInResult(TelegramBot account, String username, long userid) { } /** * 执行 bot 初始化 @@ -93,6 +101,7 @@ public class MornyCoeur { final LogInResult loginResult = login(botKey, botUsername); this.account = loginResult.account; this.username = loginResult.username; + this.userid = loginResult.userid; this.trusted = new MornyTrusted(master, trustedChat, trustedRDinner); StringBuilder trustedReadersDinnerIds = new StringBuilder(); trusted.getTrustedReadersOfDinnerSet().forEach(id -> trustedReadersDinnerIds.append("\n ").append(id)); @@ -131,24 +140,24 @@ public class MornyCoeur { boolean isAutomaticResetCommandList, boolean isRemoveCommandListWhenExit ) { if (INSTANCE == null) { - logger.info("System Starting"); + logger.info("Coeur Starting"); INSTANCE = new MornyCoeur( botKey, botUsername, master, trustedChat, trustedRDinner, latestEventTimestamp, isRemoveCommandListWhenExit ); - TrackerDataManager.init(); + MornyDaemons.start(); EventListeners.registerAllListeners(); INSTANCE.account.setUpdatesListener(OnUpdate::onNormalUpdate); if (isAutomaticResetCommandList) { logger.info("resetting telegram command list"); commandManager().automaticUpdateList(); } - logger.info("System start complete"); + logger.info("Coeur start complete"); return; } - logger.error("System already started coeur!!!"); + logger.error("Coeur already started!!!"); } /** @@ -164,8 +173,7 @@ public class MornyCoeur { */ private void exitCleanup () { logger.info("clean:save tracker data."); - TrackerDataManager.DAEMON.interrupt(); - TrackerDataManager.trackingLock.lock(); + MornyDaemons.stop(); if (isRemoveCommandListWhenExit) { commandManager.automaticRemoveList(); } @@ -195,11 +203,11 @@ public class MornyCoeur { for (int i = 1; i < 4; i++) { if (i != 1) logger.info("retrying..."); try { - final String username = account.execute(new GetMe()).user().username(); - if (requireName != null && !requireName.equals(username)) - throw new RuntimeException("Required the bot @" + requireName + " but @" + username + " logged in!"); - logger.info("Succeed login to @" + username); - return new LogInResult(account, username); + final User remote = account.execute(new GetMe()).user(); + if (requireName != null && !requireName.equals(remote.username())) + throw new RuntimeException("Required the bot @" + requireName + " but @" + remote.username() + " logged in!"); + logger.info("Succeed login to @" + remote.username()); + return new LogInResult(account, remote.username(), remote.id()); } catch (Exception e) { e.printStackTrace(System.out); logger.error("login failed."); @@ -272,4 +280,6 @@ public class MornyCoeur { return INSTANCE.extraActionInstance; } + public static long getUserid () { return INSTANCE.userid; } + } diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java index 518c9b4..bec90e2 100644 --- a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java +++ b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java @@ -18,10 +18,8 @@ import static cc.sukazyo.cono.morny.Log.logger; */ public class ServerMain { - private static boolean versionEchoMode = false; - private static boolean welcomeEchoMode = false; - - private static boolean showWelcome = true; + public static final String PROP_TOKEN_KEY = "TELEGRAM_BOT_API_TOKEN"; + public static final String PROP_TOKEN_MORNY_KEY = "MORNY_TG_TOKEN"; /** * 程序入口,也是参数处理器
@@ -77,6 +75,13 @@ public class ServerMain { */ public static void main (@Nonnull String[] args) { + //# + //# 启动参数设置区块 + //# + + boolean versionEchoMode = false; + boolean welcomeEchoMode = false; + boolean showWelcome = true; String key = null; String username = null; boolean outdatedBlock = false; @@ -154,6 +159,19 @@ public class ServerMain { } + String propToken = null; + String propTokenKey = null; + for (String iKey : new String[]{PROP_TOKEN_KEY, PROP_TOKEN_MORNY_KEY}) { + if (System.getenv(iKey) != null) { + propToken = System.getenv(iKey); + propTokenKey = iKey; + } + } + + //# + //# 启动相关参数的检查和处理 + //# + if (versionEchoMode) { logger.info(String.format(""" @@ -183,6 +201,14 @@ public class ServerMain { MornySystem.VERSION, MornySystem.getJarMd5(), GradleProjectConfigures.COMPILE_TIMESTAMP )); + //# + //# Coeur 参数检查和正式启动主程序 + //# + + if (propToken != null) { + key = propToken; + logger.info("Parameter set by EnvVar $"+propTokenKey); + } if (key == null) { logger.info("Parameter required has no value:\n --token."); return; diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.java new file mode 100644 index 0000000..7c66a8f --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/DirectMsgClear.java @@ -0,0 +1,58 @@ +package cc.sukazyo.cono.morny.bot.command; + +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.untitled.util.telegram.object.InputCommand; +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.DeleteMessage; +import com.pengrad.telegrambot.request.GetChatMember; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import static cc.sukazyo.cono.morny.Log.logger; + +public class DirectMsgClear implements ISimpleCommand { + + @Nonnull @Override public String getName () { return "r"; } + + @Nullable @Override public String[] getAliases () { return new String[0]; } + + @Override + public void execute (@Nonnull InputCommand command, @Nonnull Update event) { + + logger.debug("Executing command /r"); + if (event.message().replyToMessage() == null) return; + logger.trace("Message is a reply"); + if (event.message().replyToMessage().from().id() != MornyCoeur.getUserid()) return; + logger.trace("Message is from me"); + if (System.currentTimeMillis()/1000 - event.message().replyToMessage().date() > 48*60*60) return; + logger.trace("Message is not older than 48 hours"); + + final boolean isTrusted = MornyCoeur.trustedInstance().isTrusted(event.message().from().id()); + + if ( + isTrusted || ( + event.message().replyToMessage().replyToMessage() != null && + event.message().replyToMessage().replyToMessage().from().id().equals(event.message().from().id()) + ) + ) { + + MornyCoeur.extra().exec(new DeleteMessage( + event.message().chat().id(), event.message().replyToMessage().messageId() + )); + if (event.message().chat().type() == Chat.Type.Private || ( + MornyCoeur.extra().exec( + new GetChatMember(event.message().chat().id(), event.message().from().id()) + ).chatMember().canDeleteMessages() + )) { + MornyCoeur.extra().exec(new DeleteMessage( + event.message().chat().id(), event.message().messageId() + )); + } + + } else logger.trace("User is not trusted"); + + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java index b43df8d..18edc7b 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java @@ -18,7 +18,7 @@ import javax.annotation.Nullable; */ public class EventHack implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/event_hack"; } + @Nonnull @Override public String getName () { return "event_hack"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return "[(user|group|any)]"; } @Nonnull @Override public String getDescription () { return "输出 bot 下一个获取到的事件序列化数据"; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java index 627b6c3..1b406c7 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.java @@ -15,7 +15,7 @@ import javax.annotation.Nullable; public class GetUsernameAndId implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/user"; } + @Nonnull @Override public String getName () { return "user"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return "[userid]"; } @Nonnull @Override public String getDescription () { return "获取指定或回复的用户相关信息"; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java index 7a47390..1390096 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java @@ -20,8 +20,11 @@ import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; */ public class Ip186Query { + public static final String CMD_IP = "ip"; + public static final String CMD_WHOIS = "whois"; + public static class Ip implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/ip"; } + @Nonnull @Override public String getName () { return CMD_IP; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Nonnull @Override public String getParamRule () { return "[ip]"; } @Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询 ip 资料"; } @@ -29,7 +32,7 @@ public class Ip186Query { } public static class Whois implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/whois"; } + @Nonnull @Override public String getName () { return CMD_WHOIS; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Nonnull @Override public String getParamRule () { return "[domain]"; } @Nonnull @Override public String getDescription () { return "通过 https://ip.186526.xyz 查询域名资料"; } @@ -62,8 +65,8 @@ public class Ip186Query { try { IP186QueryResponse response = switch (command.getCommand()) { - case "/ip" -> IP186QueryHandler.queryIp(arg); - case "/whois" -> IP186QueryHandler.queryWhoisPretty(arg); + case CMD_IP -> IP186QueryHandler.queryIp(arg); + case CMD_WHOIS -> IP186QueryHandler.queryWhoisPretty(arg); default -> throw new IllegalArgumentException("Unknown 186-IP query method " + command.getCommand()); }; MornyCoeur.extra().exec(new SendMessage( 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 69173d4..f61c7f6 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 @@ -61,6 +61,7 @@ public class MornyCommands { register( new ON(), new Hello(), + new HelloOnStart(), new GetUsernameAndId(), new EventHack(), new Nbnhhsh(), @@ -73,6 +74,11 @@ public class MornyCommands { new Exit() ); + // 特殊的命令 + register( + new DirectMsgClear() + ); + // 统一注册这些奇怪的东西&.& register( new 喵呜.抱抱(), @@ -153,7 +159,7 @@ public class MornyCommands { /// private static class ON implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/o"; } + @Nonnull @Override public String getName () { return "o"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return ""; } @@ -169,12 +175,13 @@ public class MornyCommands { } private static class Hello implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/hello"; } - @Nullable @Override public String[] getAliases () { return new String[]{"/hi"}; } + @Nonnull @Override public String getName () { return "hello"; } + @Nullable @Override public String[] getAliases () { return new String[]{"hi"}; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "打招呼"; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandHelloExec(event); } } + private static class HelloOnStart implements ISimpleCommand { @Nonnull @Override public String getName () { return "start"; }@Nullable @Override public String[] getAliases () { return new String[0]; }@Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandHelloExec(event); }} private static void onCommandHelloExec (@Nonnull Update event) { MornyCoeur.extra().exec(new SendSticker( event.message().chat().id(), @@ -184,7 +191,7 @@ public class MornyCommands { } private static class Exit implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/exit"; } + @Nonnull @Override public String getName () { return "exit"; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "关闭 Bot (仅可信成员)"; } @@ -210,7 +217,7 @@ public class MornyCommands { } private static class Version implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/version"; } + @Nonnull @Override public String getName () { return "version"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "检查 Bot 版本信息"; } @@ -237,7 +244,7 @@ public class MornyCommands { } private static class MornyRuntime implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/runtime"; } + @Nonnull @Override public String getName () { return "runtime"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "获取 Bot 运行时信息(包括版本号)"; } @@ -260,12 +267,12 @@ public class MornyCommands { - %s - %s - %s - - %d cores java runtime: - %s - %s vm memory: - %d / %d MB + - %d cores coeur version: - %s - %s @@ -278,15 +285,15 @@ public class MornyCommands { - [%d]""", // system escapeHtml(hostname), - escapeHtml(System.getProperty("os.name")), + escapeHtml(String.format("%s (%s)", System.getProperty("os.name"), System.getProperty("os.arch"))), escapeHtml(System.getProperty("os.version")), - Runtime.getRuntime().availableProcessors(), // java - escapeHtml(System.getProperty("java.vm.name")), - escapeHtml(System.getProperty("java.version")), + escapeHtml(System.getProperty("java.vm.vendor")+"."+System.getProperty("java.vm.name")), + escapeHtml(System.getProperty("java.vm.version")), // memory Runtime.getRuntime().totalMemory() / 1024 / 1024, Runtime.getRuntime().maxMemory() / 1024 / 1024, + Runtime.getRuntime().availableProcessors(), // version escapeHtml(MornySystem.VERSION), escapeHtml(MornySystem.getJarMd5()), @@ -302,7 +309,7 @@ public class MornyCommands { } private static class Jrrp implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/jrrp"; } + @Nonnull @Override public String getName () { return "jrrp"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "获取 (假的) jrrp"; } @@ -322,7 +329,7 @@ public class MornyCommands { } private static class SaveData implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/save"; } + @Nonnull @Override public String getName () { return "save"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return ""; } @Nonnull @Override public String getDescription () { return "保存缓存数据到文件(仅可信成员)"; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java index b72127d..fced75b 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java @@ -16,7 +16,7 @@ import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; public class Nbnhhsh implements ITelegramCommand { - @Nonnull @Override public String getName () { return "/nbnhhsh"; } + @Nonnull @Override public String getName () { return "nbnhhsh"; } @Nullable @Override public String[] getAliases () { return null; } @Nonnull @Override public String getParamRule () { return "[text]"; } @Nonnull @Override public String getDescription () { return "检索文本内 nbnhhsh 词条"; } 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 ca31006..67d644a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java @@ -13,7 +13,7 @@ import javax.annotation.Nullable; public class 喵呜 { public static class 抱抱 implements ISimpleCommand { - @Nonnull @Override public String getName () { return "/抱抱"; } + @Nonnull @Override public String getName () { return "抱抱"; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyCoeur.extra().exec(new SendMessage( @@ -24,7 +24,7 @@ public class 喵呜 { } public static class 揉揉 implements ISimpleCommand { - @Nonnull @Override public String getName () { return "/揉揉"; } + @Nonnull @Override public String getName () { return "揉揉"; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyCoeur.extra().exec(new SendMessage( @@ -35,7 +35,7 @@ public class 喵呜 { } public static class 蹭蹭 implements ISimpleCommand { - @Nonnull @Override public String getName () { return "/蹭蹭"; } + @Nonnull @Override public String getName () { return "蹭蹭"; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyCoeur.extra().exec(new SendMessage( @@ -46,7 +46,7 @@ public class 喵呜 { } public static class 贴贴 implements ISimpleCommand { - @Nonnull @Override public String getName () { return "/贴贴"; } + @Nonnull @Override public String getName () { return "贴贴"; } @Nullable @Override public String[] getAliases () { return new String[0]; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { MornyCoeur.extra().exec(new SendMessage( 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 e06f66a..a8ad800 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java @@ -13,7 +13,7 @@ import java.util.concurrent.ThreadLocalRandom; public class 私わね implements ISimpleCommand { @Nonnull - @Override public String getName () { return "/me"; } + @Override public String getName () { return "me"; } @Nullable @Override public String[] getAliases () { return null; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java index 4eca5a5..53d6c72 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/EventListeners.java @@ -12,6 +12,8 @@ public class EventListeners { public static final OnCallMe CALL_ME = new OnCallMe(); public static final OnEventHackHandle EVENT_HACK_HANDLE = new OnEventHackHandle(); public static final OnKuohuanhuanNeedSleep KUOHUANHUAN_NEED_SLEEP = new OnKuohuanhuanNeedSleep(); + public static final OnUserRandoms USER_RANDOMS = new OnUserRandoms(); + public static final OnCallMsgSend CALL_MSG_SEND = new OnCallMsgSend(); public static void registerAllListeners () { EventListenerManager.addListener( @@ -19,9 +21,11 @@ public class EventListeners { UPDATE_TIMESTAMP_OFFSET_LOCK, KUOHUANHUAN_NEED_SLEEP, COMMANDS_LISTENER, + USER_RANDOMS, USER_SLASH_ACTION, INLINE_QUERY, CALL_ME, + CALL_MSG_SEND, EVENT_HACK_HANDLE ); } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java index 9c3ed6e..6b26436 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java @@ -1,7 +1,7 @@ package cc.sukazyo.cono.morny.bot.event; import cc.sukazyo.cono.morny.bot.api.EventListener; -import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; +import cc.sukazyo.cono.morny.daemon.TrackerDataManager; import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Update; 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 new file mode 100644 index 0000000..b5d7a34 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java @@ -0,0 +1,220 @@ +package cc.sukazyo.cono.morny.bot.event; + +import java.util.ArrayList; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.pengrad.telegrambot.model.Chat; +import com.pengrad.telegrambot.model.Message; +import com.pengrad.telegrambot.model.MessageEntity; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.model.request.ParseMode; +import com.pengrad.telegrambot.request.GetChat; +import com.pengrad.telegrambot.request.SendMessage; +import com.pengrad.telegrambot.request.SendSticker; + +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.bot.api.EventListener; +import cc.sukazyo.cono.morny.data.TelegramStickers; +import com.pengrad.telegrambot.response.GetChatResponse; +import com.pengrad.telegrambot.response.SendResponse; + +import static cc.sukazyo.untitled.util.telegram.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 record MessageToSend ( + String message, + MessageEntity[] entities, + ParseMode parseMode, + long targetId + ) { } + + @Override + public boolean onMessage(Update update) { + + // 执行体检查 + if (update.message().chat().type() != Chat.Type.Private) return false; + if (update.message().text() == null) return false; + if (!update.message().text().startsWith("*msg")) return false; + + // 权限检查 + if (!MornyCoeur.trustedInstance().isTrusted(update.message().from().id())) { + MornyCoeur.extra().exec(new SendSticker( + update.message().chat().id(), + TelegramStickers.ID_403 + ).replyToMessageId(update.message().messageId())); + return true; + } + + Message msgsendReqRaw; // 用户书写的发送请求原文 + MessageToSend msgsendReqBody; // 解析后的发送请求实例 + + // *msgsend 发送标识 + // 处理发送要求 + if (update.message().text().equals("*msgsend")) { + // 发送体处理 + if (update.message().replyToMessage() == null) return answer404(update); + msgsendReqBody = parseRequest(update.message().replyToMessage()); + if (msgsendReqBody == null) return answer404(update); + // 执行发送任务 + SendResponse sendResponse = MornyCoeur.getAccount().execute(parseMessageToSend(msgsendReqBody)); + if (!sendResponse.isOk()) { // 发送失败 + MornyCoeur.extra().exec(new SendMessage( + update.message().chat().id(), + String.format(""" + %d FAILED + %s""", + sendResponse.errorCode(), + sendResponse.description() + ) + ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML)); + } else { // 发送成功信号 + MornyCoeur.extra().exec(new SendSticker( + update.message().chat().id(), + TelegramStickers.ID_SENT + ).replyToMessageId(update.message().messageId())); + } + return true; + // 发送完成/失败 - 事件结束 + } + + // *msg 检查标识 + if (update.message().text().equals("*msg")) { // 处理对曾经的原文的检查 + if (update.message().replyToMessage() == null) { + return answer404(update); + } + msgsendReqRaw = update.message().replyToMessage(); + } else if (update.message().text().startsWith("*msg")) { // 对接受到的原文进行检查 + msgsendReqRaw = update.message(); + } else { + return answer404(update); // 未定义的动作 + } + + // 对发送请求的用户原文进行解析 + msgsendReqBody = parseRequest(msgsendReqRaw); + if (msgsendReqBody == null) { + return answer404(update); + } + + // 输出发送目标信息 + GetChatResponse targetChatReq = MornyCoeur.getAccount().execute(new GetChat(msgsendReqBody.targetId())); + if (!targetChatReq.isOk()) { + MornyCoeur.extra().exec(new SendMessage( + update.message().chat().id(), + String.format(""" + %d FAILED + %s""", + targetChatReq.errorCode(), + targetChatReq.description() + ) + ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML)); + } else { + MornyCoeur.extra().exec(new SendMessage( + update.message().chat().id(), + targetChatReq.chat().type() == Chat.Type.Private ? ( + String.format(""" + %d@%s + 🔒 %s %s""", + msgsendReqBody.targetId(), + escapeHtml(targetChatReq.chat().type().name()), + escapeHtml(targetChatReq.chat().firstName()+(targetChatReq.chat().lastName()==null?"":" "+targetChatReq.chat().lastName())), + targetChatReq.chat().username()==null? + String.format("@@", targetChatReq.chat().id()): + (escapeHtml("@"+targetChatReq.chat().username())) + ) + ) : ( + String.format(""" + %d@%s::: + %s %s%s""", + msgsendReqBody.targetId(), + escapeHtml(targetChatReq.chat().type().name()), + switch (targetChatReq.chat().type()) { + case group -> "💭"; + case channel -> "📢"; + case supergroup -> "💬"; + default -> "⭕️"; + }, + escapeHtml(targetChatReq.chat().title()), + targetChatReq.chat().username() != null?String.format( + " @%s", escapeHtml(targetChatReq.chat().username()) + ):"" + ) + ) + ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML)); + } + // 发送文本测试 + SendResponse testSendResp = MornyCoeur.getAccount().execute( + parseMessageToSend(msgsendReqBody, update.message().chat().id()).replyToMessageId(update.message().messageId()) + ); + if (!testSendResp.isOk()) { + MornyCoeur.extra().exec(new SendMessage( + update.message().chat().id(), + String.format(""" + %d FAILED + %s""", + testSendResp.errorCode(), + testSendResp.description() + ) + ).replyToMessageId(update.message().messageId()).parseMode(ParseMode.HTML)); + } + + return true; + + } + + @Nullable + private static MessageToSend parseRequest (@Nonnull Message requestBody) { + + final Matcher matcher = REGEX_MSG_SENDREQ_DATA_HEAD.matcher(requestBody.text()); + if (matcher.matches()) { + long targetId = Long.parseLong(matcher.group(1)); + ParseMode parseMode = matcher.group(2) == null ? null : switch (matcher.group(2)) { + case "*markdown", "*md", "*m↓" -> ParseMode.MarkdownV2; + case "*md1" -> ParseMode.Markdown; + case "*html" -> ParseMode.HTML; + default -> null; + }; + final int offset = "*msg".length()+matcher.group(1).length()+(matcher.group(2)==null?0:matcher.group(2).length())+1; + final ArrayList entities = new ArrayList<>(); + if (requestBody.entities() != null) for (MessageEntity entity : requestBody.entities()) { + final MessageEntity parsed = new MessageEntity(entity.type(), entity.offset() - offset, entity.length()); + if (entity.url() != null) parsed.url(entity.url()); + if (entity.user() != null) parsed.user(entity.user()); + if (entity.language() != null) parsed.language(entity.language()); + entities.add(parsed); + } + return new MessageToSend(matcher.group(3), entities.toArray(MessageEntity[]::new), parseMode, targetId); + } + + return null; + + } + + @Nonnull + private static SendMessage parseMessageToSend (@Nonnull MessageToSend body) { + return parseMessageToSend(body, body.targetId); + } + + @Nonnull + private static SendMessage parseMessageToSend (@Nonnull MessageToSend body, long targetId) { + SendMessage sendingBody = new SendMessage(targetId, body.message); + if (body.entities != null) sendingBody.entities(body.entities); + if (body.parseMode != null) sendingBody.parseMode(body.parseMode); + return sendingBody; + } + + private static boolean answer404 (@Nonnull Update update) { + MornyCoeur.extra().exec(new SendSticker( + update.message().chat().id(), + TelegramStickers.ID_404 + ).replyToMessageId(update.message().messageId())); + return true; + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.java index e0f2419..0c632ab 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnTelegramCommand.java @@ -12,10 +12,10 @@ public class OnTelegramCommand extends EventListener { @Override public boolean onMessage (@Nonnull Update event) { - if (event.message().text() == null) { - return false; // 检测到无消息文本,忽略掉命令处理 + if (event.message().text() == null || !event.message().text().startsWith("/")) { + return false; // 检测到非(命令格式)文本,忽略掉命令处理 } - final InputCommand command = new InputCommand(event.message().text()); + final InputCommand command = new InputCommand(event.message().text().substring(1)); if (command.getTarget() != null && !MornyCoeur.getUsername().equals(command.getTarget())) { return true; // 检测到命令并非针对 morny,退出整个事件处理链 } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java new file mode 100644 index 0000000..3655a97 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserRandoms.java @@ -0,0 +1,52 @@ +package cc.sukazyo.cono.morny.bot.event; + +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.bot.api.EventListener; +import cc.sukazyo.untitled.util.command.CommonCommand; +import com.pengrad.telegrambot.model.Update; +import com.pengrad.telegrambot.request.SendMessage; +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class OnUserRandoms extends EventListener { + + private static final Pattern USER_OR_CN_QUERY = Pattern.compile("(.+)还是(.+)"); + private static final Pattern USER_OR_EN_QUERY = Pattern.compile("(.+)or(.+)"); + + @Override + public boolean onMessage (@NotNull Update update) { + + if (update.message().text() == null) return false; + if (!update.message().text().startsWith("/")) return false; + + final String[] preProcess = CommonCommand.format(update.message().text()); + if (preProcess.length > 1) return false; + final String query = preProcess[0]; + + // ----- START CODE BLOCK COMMENT ----- + // 这里实现思路和代码优化有至少一半是 copilot 和 IDEA 提供的 + // 实现思路都可以从人类手里抢一半贡献太恐怖了aba + String result = null; + final Matcher matcher; + if (query.contains("还是")) { + matcher = USER_OR_CN_QUERY.matcher(query); + } else { + matcher = USER_OR_EN_QUERY.matcher(query); + } + if (matcher.find()) { + result = ThreadLocalRandom.current().nextBoolean() ? matcher.group(1) : matcher.group(2); + } + // ----- STOP CODE BLOCK COMMENT ----- + + if (result == null) return false; + MornyCoeur.extra().exec(new SendMessage( + update.message().chat().id(), result + ).replyToMessageId(update.message().messageId())); + return true; + + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java b/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java new file mode 100644 index 0000000..f081bf2 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java @@ -0,0 +1,47 @@ +package cc.sukazyo.cono.morny.daemon; + +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.data.TelegramStickers; +import com.pengrad.telegrambot.request.SendSticker; + +import static cc.sukazyo.cono.morny.Log.logger; + +public class MedicationTimer extends Thread { + + public static final long NOTIFY_RECEIVE_CHAT = 5028252995L; + + MedicationTimer () { + super("TIMER_Medication"); + } + + @Override + public void run () { + logger.info("MedicationTimer started"); + while (!interrupted()) { + try { + waitToNextRoutine(); + sendNotification(); + } catch (InterruptedException e) { + interrupt(); + logger.info("MedicationTimer was interrupted, will be exit now"); + } catch (Exception e) { + logger.error("Unexpected error occurred"); + e.printStackTrace(System.out); + } + } + logger.info("MedicationTimer stopped"); + } + + private static void sendNotification () { + MornyCoeur.extra().exec(new SendSticker(NOTIFY_RECEIVE_CHAT, TelegramStickers.ID_PROGYNOVA)); + } + + private static long calcNextRoutineTimestamp () { + return ((System.currentTimeMillis()+8*60*60*1000) / (12*60*60*1000) + 1) * 12*60*60*1000 - 8*60*60*1000; + } + + private void waitToNextRoutine () throws InterruptedException { + sleep(calcNextRoutineTimestamp() - System.currentTimeMillis()); + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java new file mode 100644 index 0000000..7fe2678 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MornyDaemons.java @@ -0,0 +1,31 @@ +package cc.sukazyo.cono.morny.daemon; + +import static cc.sukazyo.cono.morny.Log.logger; + +public class MornyDaemons { + + static MedicationTimer medicationTimerInstance; + + public static void start () { + logger.info("ALL Morny Daemons starting..."); + TrackerDataManager.init(); + medicationTimerInstance = new MedicationTimer(); + medicationTimerInstance.start(); + logger.info("Morny Daemons started."); + } + + public static void stop () { + + logger.info("ALL Morny Daemons stopping..."); + + TrackerDataManager.DAEMON.interrupt(); + medicationTimerInstance.interrupt(); + + TrackerDataManager.trackingLock.lock(); + try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); } + + logger.info("ALL Morny Daemons STOPPED."); + + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java b/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java similarity index 97% rename from src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java rename to src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java index 7b2372f..d0f8ab0 100644 --- a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/TrackerDataManager.java @@ -1,4 +1,4 @@ -package cc.sukazyo.cono.morny.data.tracker; +package cc.sukazyo.cono.morny.daemon; import java.io.File; import java.io.FileOutputStream; @@ -27,6 +27,7 @@ public class TrackerDataManager { @Override public void run () { trackingLock.lock(); + logger.info("Tracker started."); long lastWaitTimestamp = System.currentTimeMillis(); boolean postProcess = false; do { @@ -48,6 +49,7 @@ public class TrackerDataManager { else logger.info("nothing to do yet"); } while (!postProcess); trackingLock.unlock(); + logger.info("Tracker exited."); } } diff --git a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java b/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java index c53024c..ecf4921 100644 --- a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java +++ b/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java @@ -14,5 +14,6 @@ public class TelegramStickers { public static final String ID_WAITING = "CAACAgEAAx0CSQh32gABA-8DYbh7W2VhJ490ucfZMUMrgMR2FW4AAm4nAAJ4_MYFjx6zpxJPWsQjBA"; public static final String ID_SENT = "CAACAgEAAx0CSQh32gABA--zYbiyU_wOijEitp-0tSl_k7W6l3gAAgMmAAJ4_MYF4GrompjXPx4jBA"; public static final String ID_SAVED = "CAACAgEAAx0CSQh32gABBExuYdB_G0srfhQldRWkBYxWzCOv4-IAApooAAJ4_MYFcjuNZszfQcQjBA"; + public static final String ID_PROGYNOVA = "CAACAgUAAxkBAAICm2KEuL7UQqNP7vSPCg2DHJIND6UsAAKLAwACH4WSBszIo722aQ3jJAQ"; } diff --git a/src/main/java/cc/sukazyo/cono/morny/util/TelegramUserInformation.java b/src/main/java/cc/sukazyo/cono/morny/util/TelegramUserInformation.java index 1514d56..4d0126d 100644 --- a/src/main/java/cc/sukazyo/cono/morny/util/TelegramUserInformation.java +++ b/src/main/java/cc/sukazyo/cono/morny/util/TelegramUserInformation.java @@ -1,11 +1,41 @@ package cc.sukazyo.cono.morny.util; import com.pengrad.telegrambot.model.User; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +import javax.annotation.Nullable; +import java.io.IOException; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; public class TelegramUserInformation { + public static final String DC_QUERY_SOURCE_SITE = "https://t.me/"; + public static final Pattern DC_QUERY_PROCESSOR_REGEX = Pattern.compile("(cdn[1-9]).tele(sco.pe|gram-cdn.org)"); + + private static final OkHttpClient httpClient = new OkHttpClient(); + + @Nullable + public static String getDataCenterFromUsername (String username) { + final Request request = new Request.Builder().url(DC_QUERY_SOURCE_SITE + username).build(); + try (Response response = httpClient.newCall(request).execute()) { + final ResponseBody body = response.body(); + if (body == null) return "empty upstream response"; + final Matcher matcher = DC_QUERY_PROCESSOR_REGEX.matcher(body.string()); + if (matcher.find()) { + return matcher.group(1); + } + } catch (IOException e) { + return e.getMessage(); + } + return null; + } + public static String informationOutputHTML (User user) { final StringBuilder userInformation = new StringBuilder(); @@ -16,7 +46,7 @@ public class TelegramUserInformation { user.id() )); if (user.username() == null) { - userInformation.append("\nusername : null"); + userInformation.append("\nusername : null\ndatacenter : null"); } else { userInformation.append(String.format( """ @@ -25,6 +55,10 @@ public class TelegramUserInformation { - %s""", escapeHtml(user.username()) )); + // 依赖 username 的 datacenter 查询 + final String dataCenter = getDataCenterFromUsername(user.username()); + if (dataCenter == null) { userInformation.append("\ndatacenter : null"); } + else { userInformation.append(String.format("\ndatacenter : %s", escapeHtml(dataCenter))); } } if (user.firstName() == null) { userInformation.append("\nfirstname : null");