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(
"""