diff --git a/build.gradle b/build.gradle index 1720565..35dfc91 100644 --- a/build.gradle +++ b/build.gradle @@ -43,6 +43,9 @@ task updateVersionCode { ant.replaceregexp(match:'VERSION = ["a-zA-Z0-9.\\-_+@]+;', replace:"VERSION = \"$project.version\";", flags:'g', byline:true) { fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'GradleProjectConfigures.java') } + ant.replaceregexp(match:'CODENAME = ["a-zA-Z0-9]+;', replace:"CODENAME = \"${CODENAME}\";", flags:'g', byline:true) { + fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'GradleProjectConfigures.java') + } ant.replaceregexp(match:'COMPILE_TIMESTAMP = [0-9]+L;', replace:"COMPILE_TIMESTAMP = ${System.currentTimeMillis()}L;", flags:'g', byline:true) { fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'GradleProjectConfigures.java') } diff --git a/gradle.properties b/gradle.properties index 6094b3a..5a613bb 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,8 @@ ## Core -VERSION = 0.6.4.0 +VERSION = 0.7.0.16 + +CODENAME = fuzhou # dependencies diff --git a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java index 31e41f9..37de61b 100644 --- a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java +++ b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java @@ -4,6 +4,7 @@ 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.4.0"; - public static final long COMPILE_TIMESTAMP = 1652197330102L; + public static final String VERSION = "0.7.0.16"; + public static final String CODENAME = "fuzhou"; + public static final long COMPILE_TIMESTAMP = 1654072970797L; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index 90ae3e1..ac5a6dd 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -4,9 +4,12 @@ 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.impl.FileApi; +import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.request.GetMe; import javax.annotation.Nonnull; @@ -43,7 +46,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 +68,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 初始化 @@ -74,6 +83,7 @@ public class MornyCoeur { * 单位为毫秒 */ private MornyCoeur ( + @Nullable String botApi, @Nullable String botApi4File, @Nonnull String botKey, @Nullable String botUsername, long master, long trustedChat, Set trustedRDinner, long latestEventTimestamp, @@ -90,17 +100,21 @@ public class MornyCoeur { } try { - final LogInResult loginResult = login(botKey, botUsername); + final LogInResult loginResult = login(botApi, botApi4File, 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)); logger.info(String.format(""" trusted param set: - master (id) %d - trusted chat (id) - %d""", - master, trustedChat + %d + - trusted reader-of-dinner (id)%s""", + master, trustedChat, trustedReadersDinnerIds )); } catch (Exception e) { @@ -123,29 +137,31 @@ public class MornyCoeur { * @see #MornyCoeur 程序初始化方法 */ public static void main ( + @Nullable String botApi, @Nullable String botApi4File, @Nonnull String botKey, @Nullable String botUsername, long master, long trustedChat, Set trustedRDinner, long latestEventTimestamp, boolean isAutomaticResetCommandList, boolean isRemoveCommandListWhenExit ) { if (INSTANCE == null) { - logger.info("System Starting"); + logger.info("Coeur Starting"); INSTANCE = new MornyCoeur( + botApi, botApi4File, 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!!!"); } /** @@ -161,8 +177,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(); } @@ -182,21 +197,51 @@ public class MornyCoeur { * 会通过 GetMe 动作验证是否连接上了 telegram api 服务器, * 同时也要求登录获得的 username 和 {@link #username} 声明值相等 * + * @param api bot client 将会连接到的 telegram bot api 位置 + * @param api4File bot client 将会连接到的 telegram file api 位置,如果不指定则会跟随 {@code api} 选项的设定 * @param key bot 的 api-token + * @param requireName 要求登录到的需要的 username,如果登陆后的 username 与此不同则会报错退出 * @return 成功登录后的 {@link TelegramBot} 对象 */ @Nonnull - private static LogInResult login (@Nonnull String key, @Nullable String requireName) { - final TelegramBot account = new TelegramBot(key); + private static LogInResult login ( + @Nullable String api, @Nullable String api4File, + @Nonnull String key, @Nullable String requireName + ) { + final TelegramBot.Builder accountConfig = new TelegramBot.Builder(key); + boolean isCustomApi = false; + String apiUrlSet = "https://api.telegram.org/bot"; + String api4FileUrlSet = FileApi.FILE_API; + if (api != null) { + api = api.endsWith("/") ? api.substring(0, api.length() - 1) : api; + accountConfig.apiUrl(apiUrlSet = api.endsWith("/bot")? api : api + "/bot"); + isCustomApi = true; + } + if (api4File != null) { + api4File = api4File.endsWith("/") ? api4File : api4File + "/"; + accountConfig.fileApiUrl(api4FileUrlSet = api4File.endsWith("/file/bot")? api4File : api4File + "/file/bot"); + isCustomApi = true; + } else if (api != null && !api.endsWith("/bot")) { + accountConfig.fileApiUrl(api4FileUrlSet = api + "/file/bot"); + } + if (isCustomApi) { + logger.info(String.format(""" + Telegram Bot API set to : + - %s + - %s""", + apiUrlSet, api4FileUrlSet + )); + } + final TelegramBot account = accountConfig.build(); logger.info("Trying to login..."); 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."); @@ -269,4 +314,6 @@ public class MornyCoeur { return INSTANCE.extraActionInstance; } + public static long getUserid () { return INSTANCE.userid; } + } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java index 4dbca43..e9a486d 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java @@ -18,6 +18,16 @@ public class MornySystem { */ public static final String VERSION = GradleProjectConfigures.VERSION; + /** + * Morny Coeur 当前的版本代号.
+ * 一个单个单词,一般作为一个大版本的名称,只在重大更新改变
+ * 格式保持为仅由小写字母和数字组成
+ * 有时也可能是复合词或特殊的词句
+ *
+ * 会由 gradle 任务 {@code updateVersionCode} 更新 + */ + public static final String CODENAME = GradleProjectConfigures.CODENAME; + /** * 获取程序 jar 文件的 md5-hash 值
*
diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java index 1c7009a..ba93789 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java @@ -21,7 +21,7 @@ public class MornyTrusted { */ public final long MASTER; - public final Set TRUSTED_READERS_OF_DINNER; + private final Set TRUSTED_READERS_OF_DINNER; public MornyTrusted (long master, long trustedChatId, Set trustedRDinner) { this.TRUSTED_CHAT_ID = trustedChatId; @@ -51,4 +51,8 @@ public class MornyTrusted { return TRUSTED_READERS_OF_DINNER.contains(userId); } + public Set getTrustedReadersOfDinnerSet () { + return Set.copyOf(TRUSTED_READERS_OF_DINNER); + } + } diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java index f809434..d7ec325 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"; /** * 程序入口,也是参数处理器
@@ -45,6 +43,14 @@ public class ServerMain { * {@code --username} {@link MornyCoeur#getUsername() bot 的 username} 预定义 * *
  • + * {@code --api} 设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot api server。 + * 需要注意的是如果带有后缀 {@code /bot} 则会单独设定 api server + * 而不会适应性的同时为 {@code --api-files} 设定值。 + *
  • + *
  • + * {@code --api-files} 单独设定 {@link MornyCoeur#getAccount() bot client} 使用的 telegram bot file api server + *
  • + *
  • * {@code --no-hello} 不在主程序启动时输出用于欢迎消息的字符画。 * 与 {@code --only-hello} 参数不兼容 —— 会导致程序完全没有任何输出 *
  • @@ -53,20 +59,20 @@ public class ServerMain { * 赋值为程序启动的时间,从而造成阻挡程序启动之前的消息事件处理效果。 * *
  • + * {@code --auto-cmd} (下面两个)选项 {@code --auto-cmd-list} 和 {@code --auto-cmd-remove} 的合并版本 + *
  • + *
  • * {@code --auto-cmd-list} 使 morny 在启动时自动依据程序本体更新登录 bot 的命令列表 *
  • *
  • * {@code --auto-cmd-remove} 使 morny 在关闭时自动依据程序本体删除 bot 的命令列表 *
  • * - * 除去选项之外,第一个参数会被赋值为 bot 的 telegram bot api token, - * 第二个参数会被赋值为 bot 的 username 限定名。其余的参数会被认定为无法理解。
    - *
    + * 除去选项之外,第一个参数会被赋值为 bot 的 telegram bot api token, + * 第二个参数会被赋值为 bot 的 username 限定名。其余的参数会被认定为无法理解。
    * 自 {@code 0.4.2.3},token 和 username 的赋值已被选项组支持
    - * 使用参数所进行取值的 token 和 username 已被转移至 {@code --token} 和 {@code --username} 参数, - * 或许,直接参数赋值的支持将计划在 {@code 0.4.3} 标记废弃并在 {@code 0.5} 删除。 - * 但实际上这并不影响现在的使用,选项赋值目前仍属于测试功能
    - * 但请勿混用,这将使两个赋值出现混淆并产生不可知的结果 + * 自 {@code 0.5.0.4},旧的直接通过参数为 bot token & username 赋值的方式已被删除 + * 使用参数所进行取值的 token 和 username 已被转移至 {@code --token} 和 {@code --username} 参数
    * * @see MornyCoeur#main * @since 0.4.0.0 @@ -74,6 +80,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; @@ -82,6 +95,8 @@ public class ServerMain { long trustedChat = -1001541451710L; boolean autoCmdList = false; boolean autoCmdRemove = false; + String api = null; + String api4File = null; for (int i = 0; i < args.length; i++) { @@ -92,11 +107,11 @@ public class ServerMain { outdatedBlock = true; continue; } - case "--no-hello", "-hf" -> { + case "--no-hello", "-hf", "--quiet", "-q" -> { showWelcome = false; continue; } - case "--only-hello", "-o" -> { + case "--only-hello", "-ho", "-o", "-hi" -> { welcomeEchoMode = true; continue; } @@ -104,31 +119,37 @@ public class ServerMain { versionEchoMode = true; continue; } - case "--token" -> { + case "--token", "-t" -> { i++; key = args[i]; continue; } - case "--username" -> { + case "--username", "-u" -> { i++; username = args[i]; continue; } - case "--master" -> { + case "--master", "-mm" -> { i++; master = Long.parseLong(args[i]); continue; } - case "--trusted-chat" -> { + case "--trusted-chat", "-trs" -> { i++; trustedChat = Long.parseLong(args[i]); continue; } - case "--trusted-reader-dinner" -> { + //noinspection SpellCheckingInspection + case "--trusted-reader-dinner", "-trsd" -> { i++; trustedReadersOfDinner.add(Long.parseLong(args[i])); continue; } + case "--auto-cmd", "-cmd", "-c" -> { + autoCmdList = true; + autoCmdRemove = true; + continue; + } case "--auto-cmd-list", "-ca" -> { autoCmdList = true; continue; @@ -137,6 +158,16 @@ public class ServerMain { autoCmdRemove = true; continue; } + case "--api", "-a" -> { + i++; + api = args[i]; + continue; + } + case "--api-files", "files-api", "-af" -> { + i++; + api4File = args[i]; + continue; + } } } @@ -145,18 +176,31 @@ 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(""" Morny Cono Version - version : - %s + %s %s - md5hash : %s - co.time : %d %s [UTC]""", - MornySystem.VERSION, + MornySystem.VERSION, MornySystem.CODENAME.toUpperCase(), MornySystem.getJarMd5(), GradleProjectConfigures.COMPILE_TIMESTAMP, CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0) @@ -168,11 +212,29 @@ public class ServerMain { if (showWelcome) logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII); if (welcomeEchoMode) return; + logger.info(String.format(""" + ServerMain.java Loaded >>> + - version %s (%s)(%d) + - Morny %s""", + MornySystem.VERSION, + MornySystem.getJarMd5(), GradleProjectConfigures.COMPILE_TIMESTAMP, + MornySystem.CODENAME.toUpperCase() + )); + + //# + //# 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; } MornyCoeur.main( + api, api4File, key, username, master, trustedChat, trustedReadersOfDinner, outdatedBlock?System.currentTimeMillis():0, 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..1975177 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 版本信息"; } @@ -222,12 +229,14 @@ public class MornyCommands { String.format( """ version: + - Morny %s - %s core md5_hash: - %s compile timestamp: - %d - %s [UTC]""", + escapeHtml(MornySystem.CODENAME.toUpperCase()), escapeHtml(MornySystem.VERSION), escapeHtml(MornySystem.getJarMd5()), GradleProjectConfigures.COMPILE_TIMESTAMP, @@ -237,7 +246,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,14 +269,14 @@ public class MornyCommands { - %s - %s - %s - - %d cores java runtime: - %s - %s vm memory: - %d / %d MB + - %d cores coeur version: - - %s + - %s (%s) - %s - %s [UTC] - [%d] @@ -278,17 +287,18 @@ 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.CODENAME), escapeHtml(MornySystem.getJarMd5()), escapeHtml(formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)), GradleProjectConfigures.COMPILE_TIMESTAMP, @@ -302,7 +312,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 +332,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..c50e6b2 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 @@ -11,17 +11,21 @@ public class EventListeners { public static final OnInlineQueries INLINE_QUERY = new OnInlineQueries(); 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(); + @SuppressWarnings("unused") 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( ACTIVITY_RECORDER, UPDATE_TIMESTAMP_OFFSET_LOCK, - KUOHUANHUAN_NEED_SLEEP, +// 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/bot/event/OnUserSlashAction.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java index b624a23..960ff17 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java @@ -3,7 +3,6 @@ package cc.sukazyo.cono.morny.bot.event; import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.util.tgapi.TGToStringFromMessage; -import cc.sukazyo.untitled.telegram.api.formatting.TGToString; import cc.sukazyo.untitled.util.command.CommonCommand; import cc.sukazyo.untitled.util.string.StringArrays; @@ -14,7 +13,6 @@ import com.pengrad.telegrambot.request.SendMessage; import javax.annotation.Nonnull; -import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; public class OnUserSlashAction extends EventListener { @@ -24,20 +22,21 @@ public class OnUserSlashAction extends EventListener { final String text = event.message().text(); if (text == null) return false; - if (text.startsWith("/")) { + if (text.startsWith("/")) + { /// Due to @Lapis_Apple, we stopped slash action function at .DP7 groups. /// It may be enabled after some updates when the function will not be conflicted to other bots. // if (event.message().chat().id() == ) return false; - if (event.message().chat().title() != null && event.message().chat().title().contains(".DP7")) { - logger.info(String.format(""" - Chat slash action ignored due to the following keyword. - - %s - - ".DP7\"""", - TGToString.as(event.message().chat()).toStringFullNameId() - )); - return false; - } +//{ if (event.message().chat().title() != null && event.message().chat().title().contains(".DP7")) { +// logger.info(String.format(""" +// Chat slash action ignored due to the following keyword. +// - %s +// - ".DP7\"""", +// TGToString.as(event.message().chat()).toStringFullNameId() +// )); +// return false; +// } final String[] action = CommonCommand.format(text); action[0] = action[0].substring(1); 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..36d464a --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/daemon/MedicationTimer.java @@ -0,0 +1,50 @@ +package cc.sukazyo.cono.morny.daemon; + +import cc.sukazyo.cono.morny.MornyCoeur; +import cc.sukazyo.cono.morny.data.TelegramStickers; +import com.pengrad.telegrambot.request.PinChatMessage; +import com.pengrad.telegrambot.request.SendSticker; +import com.pengrad.telegrambot.response.SendResponse; + +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 () { + SendResponse m = MornyCoeur.extra().exec(new SendSticker(NOTIFY_RECEIVE_CHAT, TelegramStickers.ID_PROGYNOVA)); + if (m.isOk()) MornyCoeur.extra().exec(new PinChatMessage(NOTIFY_RECEIVE_CHAT, m.message().messageId())); + } + + 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..efa3257 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,29 +55,19 @@ 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"); - } else { - userInformation.append(String.format( - """ - - firstname : - - %s""", - escapeHtml(user.firstName()) - )); - } - if (user.lastName() == null) { - userInformation.append("\nlastname : null"); - } else { - userInformation.append(String.format( - """ - - lastname : - - %s""", - escapeHtml(user.lastName()) - )); - } + userInformation.append(String.format( + """ + + display name : + - %s%s""", + escapeHtml(user.firstName()), + user.lastName()==null ? "" : String.format("\n- %s", escapeHtml(user.lastName())) + )); if (user.languageCode() != null) { userInformation.append(String.format( """