]} 会被转换为等长的 {@code '} 字串。
- *
- * 最终的 format 格式将会类似于以下的模样:
-[1019284827][EVT388223][INFO]Something message got:
-'''''''''''''''''''''''[INFO] - data source: 19773
-'''''''''''''''''''''''[INFO] - message raw: noh2q0jwd9j-jn-9jq92-ed
- *
- *
- * @param message 消息文本,支持多行
- * @param level log级别,考虑到对齐,推荐使用四位窄字元
- * @return 整理后的字符串
- */
- @Nonnull
- private String formatMessage (@Nonnull String message, @Nonnull String level) {
- final String prompt = String.format("[%s][%s]", System.currentTimeMillis(), Thread.currentThread().getName());
- final String levelStr = String.format("[%s]", level);
- final String newline = "\n" + StringUtils.repeatChar('\'', prompt.length()) + levelStr;
- return prompt + levelStr + message.replaceAll("\\n", newline);
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java
index 4dc90bf..c6fdf0d 100644
--- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java
+++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java
@@ -9,11 +9,10 @@ import com.pengrad.telegrambot.request.GetMe;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import static cc.sukazyo.cono.morny.Logger.logger;
+import static cc.sukazyo.cono.morny.Log.logger;
/**
* Morny Cono 核心
- *
* - 的程序化入口类,保管着 morny 的核心属性
*/
public class MornyCoeur {
@@ -37,6 +36,11 @@ public class MornyCoeur {
* 会根据这里定义的时间戳取消掉比此时间更早的事件链
*/
public static long latestEventTimestamp;
+ /**
+ * morny 主程序启动时间
+ * 用于统计数据
+ */
+ public static final long coeurStartTimestamp = System.currentTimeMillis();
/**
* bot 启动入口,执行 bot 初始化
diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java
index 49074d0..89147c3 100644
--- a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java
+++ b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java
@@ -1,25 +1,15 @@
package cc.sukazyo.cono.morny;
-import javax.annotation.Nonnull;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
+import cc.sukazyo.cono.morny.util.CommonFormatUtils;
-import static cc.sukazyo.cono.morny.Logger.logger;
+import javax.annotation.Nonnull;
+
+import static cc.sukazyo.cono.morny.Log.logger;
/**
* 程序启动入口
*
* 会处理程序传入的参数和选项等数据,并执行对应的启动方式
- *
- * - 第一个参数({@code args[0]})必序传递,值为 telegram-bot 的 api-token
- * - 第二个参数可选 {@code --no-hello} 和 {@code --only-hello},
- * 前者表示不输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语},
- * 后者表示只输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语}而不运行程序逻辑
- *
- * 或者,在第一个参数处使用 {@code --version} 来输出当前程序的版本信息
*
* @since 0.4.0.0
*/
@@ -120,9 +110,7 @@ public class ServerMain {
MornySystem.VERSION,
MornySystem.getJarMd5(),
GradleProjectConfigures.COMPILE_TIMESTAMP,
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant(
- Instant.ofEpochMilli(GradleProjectConfigures.COMPILE_TIMESTAMP),
- ZoneId.ofOffset("UTC", ZoneOffset.UTC)))
+ CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)
));
return;
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java
index 73e4c1a..a7c8c5e 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java
@@ -59,4 +59,8 @@ public abstract class EventListener {
return false;
}
+ public boolean onChatJoinRequest (@Nonnull Update update) {
+ return false;
+ }
+
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java
index 5ac91a6..343de29 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java
@@ -8,7 +8,7 @@ import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
-import static cc.sukazyo.cono.morny.Logger.logger;
+import static cc.sukazyo.cono.morny.Log.logger;
public class EventListenerManager {
@@ -93,4 +93,8 @@ public class EventListenerManager {
new EventPublisher(update, x -> x.onChatMemberUpdated(update)).start();
}
+ public static void publishChatJoinRequestEvent (@Nonnull Update update) {
+ new EventPublisher(update, x -> x.onChatJoinRequest(update)).start();
+ }
+
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java
index 38e4a0b..743d0da 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java
@@ -37,14 +37,17 @@ public class InputCommand {
return new InputCommand(cx.length == 1 ? null : cx[1], cx[0], args);
}
+ @Nullable
public String getTarget () {
return target;
}
+ @Nonnull
public String getCommand () {
return command;
}
+ @Nonnull
public String[] getArgs () {
return args;
}
@@ -54,6 +57,7 @@ public class InputCommand {
}
@Override
+ @Nonnull
public String toString() {
return String.format("{{%s}@{%s}#{%s}}", command, target, Arrays.toString(args));
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java
index 8427283..e03653b 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java
@@ -49,6 +49,9 @@ public class OnUpdate {
if (update.chatMember() != null) {
EventListenerManager.publishChatMemberUpdatedEvent(update);
}
+ if (update.chatJoinRequest() != null) {
+ EventListenerManager.publishChatJoinRequestEvent(update);
+ }
}
return UpdatesListener.CONFIRMED_UPDATES_ALL;
}
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 8bb693b..a08e3fe 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
@@ -8,13 +8,15 @@ public class EventListeners {
public static final OnActivityRecord ACTIVITY_RECORDER = new OnActivityRecord();
public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction();
public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock();
+ public static final OnInlineQuery INLINE_QUERY = new OnInlineQuery();
public static void registerAllListeners () {
EventListenerManager.addListener(
ACTIVITY_RECORDER,
UPDATE_TIMESTAMP_OFFSET_LOCK,
COMMANDS_LISTENER,
- USER_SLASH_ACTION
+ USER_SLASH_ACTION,
+ INLINE_QUERY
);
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java
index 012c56a..d3e6def 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java
@@ -7,6 +7,7 @@ import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.bot.api.InputCommand;
import cc.sukazyo.cono.morny.bot.event.on_commands.GetUsernameAndId;
+import cc.sukazyo.cono.morny.util.CommonFormatUtils;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage;
@@ -14,13 +15,7 @@ import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
-import java.time.Instant;
-import java.time.LocalDateTime;
-import java.time.ZoneId;
-import java.time.ZoneOffset;
-import java.time.format.DateTimeFormatter;
-
-import static cc.sukazyo.cono.morny.Logger.logger;
+import static cc.sukazyo.cono.morny.Log.logger;
public class OnCommandExecute extends EventListener {
@@ -28,6 +23,7 @@ public class OnCommandExecute extends EventListener {
private static final String HELLO_STICKER_ID = "CAACAgEAAxkBAAMnYYYWKNXO4ibo9dlsmDctHhhV6fIAAqooAAJ4_MYFJJhrHS74xUAiBA";
private static final String EXIT_STICKER_ID = "CAACAgEAAxkBAAMoYYYWt8UjvP0N405SAyvg2SQZmokAAkMiAAJ4_MYFw6yZLu06b-MiBA";
private static final String EXIT_403_STICKER_ID = "CAACAgEAAxkBAAMqYYYa_7hpXH6hMOYMX4Nh8AVYd74AAnQnAAJ4_MYFRdmmsQKLDZgiBA";
+ private static final String NON_COMMAND_QUESTION_STICKER_ID = "CAACAgEAAx0CSQh32gABA966YbRJpbmi2lCHINBDuo1DknSTsbsAAqUoAAJ4_MYFUa8SIaZriAojBA";
@Override
public boolean onMessage (@Nonnull Update event) {
@@ -55,12 +51,27 @@ public class OnCommandExecute extends EventListener {
case "/version":
onCommandVersionExec(event);
break;
+ case "/runtime":
+ onCommandRuntimeExec(event);
+ break;
default:
- return false; // 无法解析的命令,转交事件链后代处理
+ return nonCommandExecutable(event, command);
}
return true; // 命令执行成功,标记事件为已处理,退出事件链
}
+ private boolean nonCommandExecutable (Update event, InputCommand command) {
+ if (command.getTarget() == null) return false; // 无法解析的命令,转交事件链后代处理
+ else { // 无法解析的显式命令格式,报错找不到命令
+ MornyCoeur.getAccount().execute(new SendSticker(
+ event.message().chat().id(),
+ NON_COMMAND_QUESTION_STICKER_ID
+ ).replyToMessageId(event.message().messageId())
+ );
+ return true;
+ }
+ }
+
private void onCommandOnExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(),
@@ -101,18 +112,59 @@ public class OnCommandExecute extends EventListener {
event.message().chat().id(),
String.format("""
version:
-
%s
+ - %s
core md5_hash:
-
%s
+ - %s
compile timestamp:
-
%d
-
%s [UTC]
""",
+ - %d
+ - %s [UTC]
""",
MornySystem.VERSION,
MornySystem.getJarMd5(),
GradleProjectConfigures.COMPILE_TIMESTAMP,
- DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant(
- Instant.ofEpochMilli(GradleProjectConfigures.COMPILE_TIMESTAMP),
- ZoneId.ofOffset("UTC", ZoneOffset.UTC)))
+ CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)
+ )
+ ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
+ }
+
+ private void onCommandRuntimeExec (@Nonnull Update event) {
+ MornyCoeur.getAccount().execute(new SendMessage(
+ event.message().chat().id(),
+ String.format("""
+ system:
+ - %s
+ - %s
+ - %d
cores
+ java runtime:
+ - %s
+ - %s
+ memory:
+ - %d
/ %d
MB
+ morny version:
+ - %s
+ - %s
+ - %s [UTC]
+ - [%d
]
+ continuous
+ - %s
+ - [%d
]""",
+ // system
+ System.getProperty("os.name"),
+ System.getProperty("os.version"),
+ Runtime.getRuntime().availableProcessors(),
+ // java
+ System.getProperty("java.vm.name"),
+ System.getProperty("java.version"),
+ // memory
+ Runtime.getRuntime().totalMemory() / 1024 / 1024,
+ Runtime.getRuntime().maxMemory() / 1024 / 1024,
+ // version
+ MornySystem.VERSION,
+ MornySystem.getJarMd5(),
+ CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0),
+ GradleProjectConfigures.COMPILE_TIMESTAMP,
+ // continuous
+ CommonFormatUtils.formatDuration(System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp),
+ System.currentTimeMillis() - MornyCoeur.coeurStartTimestamp
)
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.java
new file mode 100644
index 0000000..6bf189e
--- /dev/null
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.java
@@ -0,0 +1,33 @@
+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.EncryptUtils;
+import com.pengrad.telegrambot.model.Update;
+import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
+import com.pengrad.telegrambot.model.request.InputTextMessageContent;
+import com.pengrad.telegrambot.model.request.ParseMode;
+import com.pengrad.telegrambot.request.AnswerInlineQuery;
+import org.jetbrains.annotations.NotNull;
+
+/**
+ * @since 0.4.1.3
+ */
+public class OnInlineQuery extends EventListener {
+
+ /**
+ * @since 0.4.1.3
+ */
+ @Override
+ public boolean onInlineQuery (@NotNull Update update) {
+ MornyCoeur.getAccount().execute(new AnswerInlineQuery(update.inlineQuery().id(), new InlineQueryResultArticle[]{
+ new InlineQueryResultArticle(
+ EncryptUtils.encryptByMD5(update.inlineQuery().query()),
+ "Raw Input",
+ new InputTextMessageContent(update.inlineQuery().query()).parseMode(ParseMode.MarkdownV2)
+ )
+ }));
+ return true;
+ }
+
+}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.java
index 2ad2fd4..7130277 100644
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.java
+++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUpdateTimestampOffsetLock.java
@@ -9,7 +9,7 @@ public class OnUpdateTimestampOffsetLock extends EventListener {
@Override
public boolean onMessage (@NotNull Update update) {
- return update.message().date() < MornyCoeur.latestEventTimestamp*1000;
+ return update.message().date() < MornyCoeur.latestEventTimestamp/1000;
}
}
diff --git a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java b/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java
index 1695a24..753944d 100644
--- a/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java
+++ b/src/main/java/cc/sukazyo/cono/morny/data/tracker/TrackerDataManager.java
@@ -10,7 +10,7 @@ import java.util.HashMap;
import java.util.TreeSet;
import java.util.concurrent.locks.ReentrantLock;
-import static cc.sukazyo.cono.morny.Logger.logger;
+import static cc.sukazyo.cono.morny.Log.logger;
public class TrackerDataManager {
@@ -40,7 +40,7 @@ public class TrackerDataManager {
}
if (interrupted()) {
postProcess = true;
- logger.warn("last tracker write in this processor!");
+ logger.info("CALLED TO EXIT! writing cache.");
}
if (record.size() != 0) {
logger.info("start writing tracker data.");
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/CommonFormatUtils.java b/src/main/java/cc/sukazyo/cono/morny/util/CommonFormatUtils.java
new file mode 100644
index 0000000..c629548
--- /dev/null
+++ b/src/main/java/cc/sukazyo/cono/morny/util/CommonFormatUtils.java
@@ -0,0 +1,28 @@
+package cc.sukazyo.cono.morny.util;
+
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.time.ZoneOffset;
+import java.time.format.DateTimeFormatter;
+
+public class CommonFormatUtils {
+
+ public static String formatDate (long timestamp, int utcOffset) {
+ return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant(
+ Instant.ofEpochMilli(timestamp),
+ ZoneId.ofOffset("UTC", ZoneOffset.ofHours(utcOffset))
+ ));
+ }
+
+ public static String formatDuration (long duration) {
+ StringBuilder sb = new StringBuilder();
+ if (duration > 1000 * 60 * 60 * 24) sb.append(duration / (1000*60*60*24)).append("d ");
+ if (duration > 1000 * 60 * 60) sb.append(duration / (1000*60*60) % 24).append("h ");
+ if (duration > 1000 * 60) sb.append(duration / (1000*60) % 60).append("min ");
+ if (duration > 1000) sb.append(duration / 1000 % 60).append("s ");
+ sb.append(duration % 1000).append("ms");
+ return sb.toString();
+ }
+
+}
diff --git a/src/main/java/cc/sukazyo/cono/morny/util/EncryptUtils.java b/src/main/java/cc/sukazyo/cono/morny/util/EncryptUtils.java
new file mode 100644
index 0000000..6d502f9
--- /dev/null
+++ b/src/main/java/cc/sukazyo/cono/morny/util/EncryptUtils.java
@@ -0,0 +1,61 @@
+package cc.sukazyo.cono.morny.util;
+
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+
+/**
+ * 用于数据加密或编码的工具类
+ * 显然大部分代码是抄来的
+ *
+ */
+public class EncryptUtils {
+
+ private final static String[] hexArray = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
+
+ /***
+ * 对指定的字符串进行MD5加密
+ */
+ public static String encryptByMD5(String originString) {
+ try {
+ //创建具有MD5算法的信息摘要
+ MessageDigest md = MessageDigest.getInstance("MD5");
+ //使用指定的字节数组对摘要进行最后更新,然后完成摘要计算
+ byte[] bytes = md.digest(originString.getBytes());
+ //将得到的字节数组变成字符串返回
+ String s = byteArrayToHex(bytes);
+ return s.toUpperCase();
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ /**
+ * 将字节数组转换成十六进制,并以字符串的形式返回
+ * 128位是指二进制位。二进制太长,所以一般都改写成16进制,
+ * 每一位16进制数可以代替4位二进制数,所以128位二进制数写成16进制就变成了128/4=32位。
+ */
+ private static String byteArrayToHex(byte[] b){
+ StringBuilder sb = new StringBuilder();
+ for (byte value : b) {
+ sb.append(byteToHex(value));
+ }
+ return sb.toString();
+ }
+
+ /**
+ * 将一个字节转换成十六进制,并以字符串的形式返回
+ */
+ public static String byteToHex(byte b) {
+ int n = b;
+ if (n < 0)
+ n = n + 256;
+ int d1 = n / 16;
+ int d2 = n % 16;
+ return hexArray[d1]+hexArray[d2];
+ }
+
+}