diff --git a/.dockerignore b/.dockerignore
index df3d0f1..e23a3f5 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,6 +10,8 @@
#build
/build/
/bin/
+.metals/
+.bloop/
.project
lcoal.properties
diff --git a/.gitignore b/.gitignore
index df3d0f1..e23a3f5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,6 +10,8 @@
#build
/build/
/bin/
+.metals/
+.bloop/
.project
lcoal.properties
diff --git a/build.gradle b/build.gradle
index aff5df9..a15ac59 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,5 +1,5 @@
plugins {
- id 'java'
+ id 'scala'
id 'java-library'
id 'application'
id 'maven-publish'
@@ -50,7 +50,9 @@ final long proj_code_time = proj_clean ? grgit.head().dateTime.toInstant().toEpo
final JavaVersion proj_java = JavaVersion.VERSION_17
final Charset proj_file_encoding = StandardCharsets.UTF_8
-
+final proj_scala_api = 3
+//final proj_scala_lib = proj_scala_api+'.4.0-RC1-bin-20230901-89e8dba-NIGHTLY'
+final proj_scala_lib = proj_scala_api+'.3.1-RC7'
String publish_local_url = null
String publish_remote_url = null
String publish_remote_username = null
@@ -72,6 +74,7 @@ repositories {
dependencies {
+ api "org.scala-lang:scala3-library_3:${proj_scala_lib}"
compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${lib_spotbugs_v}"
implementation "cc.sukazyo:messiva:${lib_messiva_v}"
@@ -86,8 +89,30 @@ dependencies {
}
-application {
- mainClass = proj_application_main
+sourceSets {
+ main {
+ scala { srcDirs = ['src/main/scala', 'src/main/old'] }
+ }
+}
+
+scala {
+
+ compileJava {
+
+ sourceCompatibility '17'
+ targetCompatibility '17'
+
+ options.encoding = proj_file_encoding.name()
+
+ }
+
+ compileScala {
+
+ options.encoding = proj_file_encoding.name()
+ scalaCompileOptions.encoding = proj_file_encoding.name()
+
+ }
+
}
test {
@@ -97,27 +122,8 @@ test {
}
}
-java {
-
- sourceCompatibility proj_java
- targetCompatibility proj_java
-
- withSourcesJar()
-
-}
-
-tasks.withType(JavaCompile).configureEach {
- options.encoding = proj_file_encoding.name()
-}
-
-tasks.withType(Javadoc).configureEach {
- options.encoding = proj_file_encoding.name()
- options.docEncoding = proj_file_encoding.name()
- options.charSet = proj_file_encoding.name()
-}
-
-tasks.test {
- useJUnitPlatform()
+application {
+ mainClass = proj_application_main
}
buildConfig {
diff --git a/gradle.properties b/gradle.properties
index 2996049..908c69b 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -7,8 +7,8 @@ MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s
VERSION = 1.0.0-RC4
-USE_DELTA = false
-VERSION_DELTA =
+USE_DELTA = true
+VERSION_DELTA = scalaport1
CODENAME = beiping
diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java
deleted file mode 100644
index 208ed1b..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java
+++ /dev/null
@@ -1,296 +0,0 @@
-package cc.sukazyo.cono.morny;
-
-import cc.sukazyo.cono.morny.util.CommonFormat;
-
-import javax.annotation.Nonnull;
-
-import java.time.ZoneOffset;
-import java.util.ArrayList;
-import java.util.List;
-
-import static cc.sukazyo.cono.morny.Log.logger;
-
-/**
- * 程序启动入口
- *
- * 会处理程序传入的参数和选项等数据,并执行对应的启动方式
- *
- * @since 0.4.0.0
- */
-public class ServerMain {
-
- public static final long systemStartupTime = System.currentTimeMillis();
-
- private static final String THREAD_MORNY_INIT = "morny-init";
-
- /**
- * 程序入口,也是参数处理器
- *
- * 以 {@code -} 开头的参数会被解析为选项
- *
- * 支持以下选项
- *
" + MsgEscape.escapeHtml(resultString) + "
"
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- } else {
- MornyCoeur.extra().exec(new SendDocument(
- event.message().chat().id(),
- result
- ).fileName(resultName).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
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
deleted file mode 100644
index dbcfc81..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/EventHack.java
+++ /dev/null
@@ -1,99 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.MornyTrusted;
-import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * {@link OnEventHackHandle} 的命令行前端
- * @since 0.4.2.0
- */
-public class EventHack implements ITelegramCommand {
-
- @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 下一个获取到的事件序列化数据"; }
-
- /**
- * {@link OnEventHackHandle} 的命令行前端$__channel_identify
"
- ));
- return;
- }
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- TelegramUserInformation.informationOutputHTML(user)
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
-
- }
-
-}
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
deleted file mode 100644
index 4d5b4f3..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Ip186Query.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse;
-import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-
-/**
- * {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端
- * @since 0.4.2.10
- */
-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 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 资料"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { exec(event, command); }
- }
-
- public static class Whois implements ITelegramCommand {
- @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 查询域名资料"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { exec(event, command); }
- }
-
- private static void exec (@Nonnull Update event, @Nonnull InputCommand command) {
-
- String arg = null;
- if (!command.hasArgs()) {
- if (event.message().replyToMessage() != null) {
- arg = event.message().replyToMessage().text();
- }
- } else if (command.getArgs().length > 1) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] Too much arguments."
- ).replyToMessageId(event.message().messageId()));
- return;
- } else {
- arg = command.getArgs()[0];
- }
- if (arg == null) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Unavailable] No ip defined."
- ).replyToMessageId(event.message().messageId()));
- return;
- }
-
- try {
- IP186QueryResponse response = switch (command.getCommand()) {
- 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(
- event.message().chat().id(),
- escapeHtml(response.url()) + "\n" + escapeHtml(response.body()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- } catch (Exception e) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Exception] in query:\n" + escapeHtml(e.getMessage()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.java
deleted file mode 100644
index 245bae8..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.java
+++ /dev/null
@@ -1,44 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendPhoto;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * The implementation of Telegram special command `/start`.
- *
- * @see MornyInformation related class where some data comes from.
- *
- * @since 1.0.0-RC4
- */
-public class MornyInfoOnHello implements ISimpleCommand {
-
- @Nonnull @Override public String getName() { return "start"; }
- @Nullable @Override public String[] getAliases() { return new String[0]; }
- // @Override public String getParamRule() { return ""; }
- // @Override public String getDescription() { return "" }
-
- @Override
- public void execute(@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendPhoto(
- event.message().chat().id(),
- MornyInformation.getAboutPic()
- ).caption("""
- 欢迎使用 Morny Cono,来自安妮的侍从小鼠。
- Morny 具有各种各样的功能。
-
- ————————————————
- %s
- ————————————————
-
- (你可以随时通过 /info 重新获得这些信息)""".formatted(MornyInformation.getMornyAboutLinksHTML())
- ).parseMode(ParseMode.HTML));
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformation.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformation.java
deleted file mode 100644
index 6a204e0..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformation.java
+++ /dev/null
@@ -1,301 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.BuildConfig;
-import cc.sukazyo.cono.morny.MornyAbout;
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.MornySystem;
-import cc.sukazyo.cono.morny.data.TelegramImages;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.ExtraAction;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendPhoto;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-import java.util.Objects;
-
-import static cc.sukazyo.cono.morny.util.CommonFormat.formatDate;
-import static cc.sukazyo.cono.morny.util.CommonFormat.formatDuration;
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-public class MornyInformation implements ITelegramCommand {
-
- private static final String SUB_STICKER = "stickers";
- private static final String SUB_RUNTIME = "runtime";
- private static final String SUB_VERSION = "version";
- private static final String SUB_VERSION_2 = "v";
-
- @Nonnull @Override public String getName () { return "info"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Nonnull @Override public String getParamRule () { return "[(version|runtime|stickers[.IDs])]"; }
- @Nonnull @Override public String getDescription () { return "输出当前 Morny 的各种信息"; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- if (!command.hasArgs()) {
- echoInfo(event.message().chat().id(), event.message().messageId());
- return;
- }
-
- final String action = command.getArgs()[0];
-
- if (action.startsWith(SUB_STICKER)) {
- echoStickers(command, event);
- } else if (action.equals(SUB_RUNTIME)) {
- echoRuntime(event);
- } else if (action.equals(SUB_VERSION) || action.equals(SUB_VERSION_2)) {
- echoVersion(event);
- } else {
- echo404(event);
- }
-
- }
-
- /**
- * Subcommand /info without params.
- *
- * @since 1.0.0-RC4
- */
- public void echoInfo (long chatId, int replayToMessage) {
- MornyCoeur.extra().exec(new SendPhoto(
- chatId,
- getAboutPic()
- ).caption("""
- Morny Cono
- 来自安妮的侍从小鼠。
- ————————————————
- %s""".formatted(getMornyAboutLinksHTML())
- ).parseMode(ParseMode.HTML).replyToMessageId(replayToMessage));
- }
-
- /**
- * subcommand /info stickers
- *
- * @see #SUB_STICKER
- */
- public void echoStickers (@Nonnull InputCommand command, @Nonnull Update event) {
- final long echoTo = event.message().chat().id();
- final int replyToMessage = event.message().messageId();
- String id = null;
- if (command.getArgs()[0].equals(SUB_STICKER)) {
- if (command.getArgs().length == 1) {
- id = "";
- } else if (command.getArgs().length == 2) {
- id = command.getArgs()[1];
- }
- } else if (command.getArgs().length == 1) {
- if (command.getArgs()[0].startsWith(SUB_STICKER+".") || command.getArgs()[0].startsWith(SUB_STICKER+"#")) {
- id = command.getArgs()[0].substring(SUB_STICKER.length()+1);
- }
- }
- if (id == null) { echo404(event); return; }
- echoStickers(id, echoTo, replyToMessage);
- }
-
- /**
- * 向 telegram 输出一个或全部 sticker.
- *
- * @param id
- * sticker 在 {@link TelegramStickers} 中的字段名。
- * 使用 {@link ""}(空字符串)(不是{@link null}) 表示输出全部 sticker
- * @param chatId 目标 chat id
- * @param messageId 要回复的消息 id,依据 {@link TelegramStickers#echoStickerByID(String, ExtraAction, long, int) 上游}
- * 逻辑,使用 {@link -1} 表示不回复消息。
- *
- * @see TelegramStickers#echoStickerByID(String, ExtraAction, long, int)
- * @see TelegramStickers#echoAllStickers(ExtraAction, long, int)
- */
- public static void echoStickers (@Nonnull String id, long chatId, int messageId) {
- if (id.isEmpty()) TelegramStickers.echoAllStickers(MornyCoeur.extra(), chatId, messageId);
- else TelegramStickers.echoStickerByID(id, MornyCoeur.extra(), chatId, messageId);
- }
-
- /**
- * Subcommand /info runtime.
- *
- * @see #SUB_RUNTIME
- *
- * @since 1.0.0-alpha4
- */
- public static void echoRuntime (@Nonnull Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- String.format("""
- system:
- - %s
- - %s
(%s
) %s
- java runtime:
- - %s
- - %s
- vm memory:
- - %d
/ %d
MB
- - %d
cores
- coeur version:
- - %s
- - %s
- - %s [UTC]
- - [%d
]
- continuous:
- - %s
- - [%d
]
- - %s [UTC]
- - [%d
]""",
- // system
- escapeHtml(getRuntimeHostName()==null ? "%s
- - %s
%s%s
- core md5_hash:
- - %s
- coding timestamp:
- - %d
- - %s [UTC]
""",
- escapeHtml(MornySystem.CODENAME.toUpperCase()),
- escapeHtml(MornySystem.VERSION_BASE),
- MornySystem.isUseDelta() ? String.format("-δ%s
", escapeHtml(Objects.requireNonNull(MornySystem.VERSION_DELTA))) : "",
- MornySystem.isGitBuild() ? "\n- git "+getVersionGitTagHtml() : "",
- escapeHtml(MornySystem.getJarMd5()),
- BuildConfig.CODE_TIMESTAMP,
- escapeHtml(formatDate(BuildConfig.CODE_TIMESTAMP, 0))
- )
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
- }
-
- /**
- * 取得 {@link MornySystem} 的 git commit 相关版本信息的 HTML 格式化标签.
- *
- * @return 格式类似于 {@code 28e8c82a.δ} 的以 HTML 方式格式化的版本号组件。
- * 其中 {@code .δ} 对应着 {@link MornySystem#isCleanBuild};
- * commit tag 字段如果支援 {@link MornySystem#currentCodePath} 则会以链接形式解析,否则则为 code 格式
- * 为了对 telegram api html 格式兼容所以不支援嵌套链接与code标签。
- * 如果 {@link MornySystem#isGitBuild} 为 {@link false},则方法会返回 {@link ""}
- *
- * @since 1.0.0-beta2
- */
- @Nonnull
- public static String getVersionGitTagHtml () {
- if (!MornySystem.isGitBuild()) return "";
- final StringBuilder g = new StringBuilder();
- final String cp = MornySystem.currentCodePath();
- if (cp == null) g.append(String.format("%s
", BuildConfig.COMMIT.substring(0, 8)));
- else g.append(String.format("%s", MornySystem.currentCodePath(), BuildConfig.COMMIT.substring(0, 8)));
- if (!MornySystem.isCleanBuild()) g.append(".δ
");
- return g.toString();
- }
-
- /**
- * 取得完整 Morny 版本的 HTML 格式化标签.
- *
- * 相比于 {@link MornySystem#VERSION_FULL},这个版本号还包含了 {@link MornySystem#CODENAME 版本 codename}。
- * 各个部分也被以 HTML 的格式进行了格式化以可以更好的在富文本中插入使用.
- * @return 基于 HTML 标签进行了格式化了的类似于
- * {@link MornySystem#VERSION_BASE 5.38.2-alpha1}{@link MornySystem#isUseDelta() -δ}{@link MornySystem#VERSION_DELTA tt}{@link MornySystem#isGitBuild() +git.}{@link #getVersionGitTagHtml() 28e8c82a.δ}*{@link MornySystem#CODENAME TOKYO}
- * 的版本号。
- * @since 1.0.0-beta2
- */
- @Nonnull
- public static String getVersionAllFullTagHtml () {
- final StringBuilder v = new StringBuilder();
- v.append("").append(MornySystem.VERSION_BASE).append("
");
- if (MornySystem.isUseDelta()) v.append("-δ").append(MornySystem.VERSION_DELTA).append("
");
- if (MornySystem.isGitBuild()) v.append("+git.").append(getVersionGitTagHtml());
- v.append("*").append(MornySystem.CODENAME.toUpperCase()).append("
");
- return v.toString();
- }
-
- /**
- * 获取 coeur 运行时的宿主机的主机名
- * @return coeur 宿主机主机名,或者 {@link null} 表示获取失败
- */
- @Nullable
- public static String getRuntimeHostName () {
- try {
- return InetAddress.getLocalHost().getHostName();
- } catch (UnknownHostException e) {
- return null;
- }
- }
-
- /**
- * Get the about-pic (intro picture or featured image) of Morny.
- *
- * @return the Telegram file binary data of the about-pic.
- * @throws IllegalStateException {@link TelegramImages.AssetsFileImage#get() get() image data} may
- * throws {@link IllegalStateException} while read error.
- */
- @Nonnull
- public static byte[] getAboutPic () {
- return TelegramImages.IMG_ABOUT.get();
- }
-
- private static void echo404 (@Nonnull Update event) {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_404
- ).replyToMessageId(event.message().messageId()));
- }
-
- /**
- * The formatted about links of Morny Cono and Morny Coeur.
- *
- * With the Telegram HTML formatting, used in /info and /start.
- * Provided the end user the links that can find resources about Morny.
- */
- @Nonnull
- public static String getMornyAboutLinksHTML () {
- return """
- source code | backup
- 反馈 / issue tracker
- 使用说明书 / user guide & docs""".formatted(
- MornyAbout.MORNY_SOURCECODE_LINK, MornyAbout.MORNY_SOURCECODE_SELF_HOSTED_MIRROR_LINK,
- MornyAbout.MORNY_ISSUE_TRACKER_LINK,
- MornyAbout.MORNY_USER_GUIDE_LINK
- );
- }
-
-}
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
deleted file mode 100644
index 20adb23..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.java
+++ /dev/null
@@ -1,80 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.NbnhhshQuery;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting;
-import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
-
-public class Nbnhhsh implements ITelegramCommand {
-
- @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 词条"; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- class TagNoContent extends Exception {}
- try {
-
- String queryTarget;
- if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null)
- queryTarget = event.message().replyToMessage().text();
- else if (command.hasArgs())
- queryTarget = stringsConnecting(command.getArgs(), " ", 0, command.getArgs().length-1);
- else {
- throw new TagNoContent();
- }
-
- NbnhhshQuery.GuessResult response = NbnhhshQuery.sendGuess(queryTarget);
-
- StringBuilder message = new StringBuilder("## Result of nbnhhsh query :");
-
- for (NbnhhshQuery.Word word : response.words) {
- if (word.trans != null && word.trans.length == 0) word.trans = null;
- if (word.inputting != null && word.inputting.length == 0) word.inputting = null;
- if (word.trans == null && word.inputting == null) continue;
- message.append("\n\n[[ ").append(escapeHtml(word.name)).append(" ]]");
- if (word.trans != null) for (String trans : word.trans) {
- message.append("\n* ").append(escapeHtml(trans)).append("");
- }
- if (word.inputting != null) {
- if (word.trans != null) message.append("\n");
- message.append(" maybe:");
- for (String trans : word.inputting) {
- message.append("\n` ").append(escapeHtml(trans)).append("");
- }
- }
- }
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- message.toString()
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
-
- } catch (TagNoContent tag) {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(), TelegramStickers.ID_404
- ).replyToMessageId(event.message().messageId()));
- } catch (Exception e) {
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "[Exception] in query:\n" + escapeHtml(e.getMessage()) + "
"
- ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
- }
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java
deleted file mode 100644
index 8039ce7..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/Testing.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-public class Testing implements ISimpleCommand {
-
- @Nonnull
- @Override
- public String getName () {
- return "test";
- }
-
- @Nullable
- @Override
- public String[] getAliases () {
- return null;
- }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
-
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- "Just a TEST command."
- ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
-
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java
deleted file mode 100644
index 692ce2a..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/喵呜.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Message;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-/**
- * WARNING that {@link cc.sukazyo.cono.morny.bot.event.OnTelegramCommand}
- * 并不能够处理非 english word 字符之外的命令.
- *
- * 出于这个限制,以下几个命令目前都无法使用
- * @see 抱抱
- * @see 揉揉
- * @see 蹭蹭
- * @see 贴贴
- */
-@SuppressWarnings("NonAsciiCharacters")
-public class 喵呜 {
-
- public static class 抱抱 implements ISimpleCommand {
- @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) {
- replyingSet(event, "抱抱", "抱抱");
- }
- }
-
- public static class 揉揉 implements ISimpleCommand {
- @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) {
- replyingSet(event, "蹭蹭", "摸摸");
- }
- }
-
- public static class 蹭蹭 implements ISimpleCommand {
- @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) {
- replyingSet(event, "揉揉", "蹭蹭");
- }
- }
-
- public static class 贴贴 implements ISimpleCommand {
- @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) {
- replyingSet(event, "贴贴", "贴贴");
- }
- }
-
- private static void replyingSet (@Nonnull Update event, @Nonnull String whileRec, @Nonnull String whileNew) {
- final boolean isNew = event.message().replyToMessage() == null;
- final Message target = isNew ? event.message() : event.message().replyToMessage();
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- isNew ? whileNew : whileRec
- ).replyToMessageId(target.messageId()).parseMode(ParseMode.HTML));
- }
-
- public static class Progynova implements ITelegramCommand {
- @Nonnull @Override public String getName () { return "install"; }
- @Nullable @Override public String[] getAliases () { return new String[0]; }
- @Nonnull @Override public String getParamRule () { return ""; }
- @Nonnull @Override public String getDescription () { return "抽取一个神秘盒子"; }
- @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().chat().id(),
- TelegramStickers.ID_PROGYNOVA
- ).replyToMessageId(event.message().messageId()));
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java
deleted file mode 100644
index 6006c0d..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/command/私わね.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package cc.sukazyo.cono.morny.bot.command;
-
-import cc.sukazyo.cono.morny.MornyCoeur;
-import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.request.SendMessage;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import static cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue;
-
-@SuppressWarnings("NonAsciiCharacters")
-public class 私わね implements ISimpleCommand {
-
- @Nonnull
- @Override public String getName () { return "me"; }
-
- @Nullable
- @Override public String[] getAliases () { return null; }
-
- @Override
- public void execute (@Nonnull InputCommand command, @Nonnull Update event) {
- if (probabilityTrue(521)) {
- // 可以接入未来的心情系统(如果有的话)
-// final String text = switch (ThreadLocalRandom.current().nextInt(11)) {
-// case 0,7,8,9,10 -> "才不是";
-// case 1,2,3,6 -> "才不是!";
-// case 4,5 -> "才不是..";
-// default -> throw new IllegalStateException("Unexpected random value in 私わね command.");
-// };
- final String text = "/打假";
- MornyCoeur.extra().exec(new SendMessage(
- event.message().chat().id(),
- text
- ).replyToMessageId(event.message().messageId()));
- }
- }
-
-}
diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMe.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMe.java
deleted file mode 100644
index 89ed0fc..0000000
--- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMe.java
+++ /dev/null
@@ -1,181 +0,0 @@
-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.data.TelegramStickers;
-import cc.sukazyo.cono.morny.util.CommonFormat;
-import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape;
-import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
-import com.pengrad.telegrambot.model.Chat;
-import com.pengrad.telegrambot.model.Message;
-import com.pengrad.telegrambot.model.Update;
-import com.pengrad.telegrambot.model.request.ParseMode;
-import com.pengrad.telegrambot.request.ForwardMessage;
-import com.pengrad.telegrambot.request.GetChat;
-import com.pengrad.telegrambot.request.SendMessage;
-import com.pengrad.telegrambot.request.SendSticker;
-import com.pengrad.telegrambot.response.SendResponse;
-
-import javax.annotation.Nonnull;
-
-/**
- * 通过 bot 呼叫主人的事件监听管理类
- * @since 0.4.2.1
- */
-public class OnCallMe extends EventListener {
-
- /**
- * 主人的 telegram user id,同时被用于 chat id
- * 跟随 {@link cc.sukazyo.cono.morny.MornyConfig#trustedMaster} 的值
- * @since 0.4.2.1
- */
- private static final long ME = MornyCoeur.config().trustedMaster;
-
- /**
- * 监听私聊 bot 的消息进行呼叫关键字匹配。
- * 如果成功,将会执行呼叫函数,并向呼叫者回显{@link TelegramStickers#ID_WAITING "已呼叫"贴纸}
- *
- * @param update 事件基础参数,消息事件所属的 tgapi:update 对象
- * @return 事件基础返回值,是否已完成处理事件:
- * 如果匹配到呼叫,则返回{@code true},反之返回{@code false}
- */
- @Override
- public boolean onMessage (@Nonnull Update update) {
- if (update.message().text() == null)
- return false;
- if (update.message().chat().type() != Chat.Type.Private)
- return false;
- switch (update.message().text().toLowerCase()) {
- case "steam", "sbeam", "sdeam" ->
- requestSteamJoin(update);
- case "hana paresu", "花宫", "内群" ->
- requestHanaParesuJoin(update);
- case "dinner", "lunch", "breakfast", "meal", "eating", "安妮今天吃什么" ->
- requestLastDinner(update);
- default -> {
- if (update.message().text().startsWith("cc::")) {
- requestCustomCall(update);
- break;
- }
- return false;
- }
- }
- MornyCoeur.extra().exec(new SendSticker(
- update.message().chat().id(),
- TelegramStickers.ID_SENT
- ).replyToMessageId(update.message().messageId())
- );
- return true;
- }
-
- /**
- * 执行 steam library 呼叫
- * 将会向 {@link #ME} 发送
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestSteamJoin (Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request STEAM LIBRARY
- from %s""",
- TGToString.as(event.message().from()).fullnameRefHtml()
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 执行花宫呼叫
- * 将会向 {@link #ME} 发送
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestHanaParesuJoin (Update event) {
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request Hana Paresu
- from %s""",
- TGToString.as(event.message().from()).fullnameRefHtml()
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 对访问最近一次的饭局的请求进行回复
- *
- * @param event 执行呼叫的tg事件
- */
- private static void requestLastDinner (Update event) {
- boolean isAllowed = false;
- Message lastDinnerData = null;
- if (MornyCoeur.trustedInstance().isTrustedForDinnerRead(event.message().from().id())) {
- lastDinnerData = MornyCoeur.extra().exec(new GetChat(MornyCoeur.config().dinnerChatId)).chat().pinnedMessage();
- SendResponse sendResp = MornyCoeur.extra().exec(new ForwardMessage(
- event.message().from().id(),
- lastDinnerData.forwardFromChat().id(),
- lastDinnerData.forwardFromMessageId()
- ));
- MornyCoeur.extra().exec(new SendMessage(
- event.message().from().id(),
- String.format("on %s [UTC+8]
\n- %s
before",
- MsgEscape.escapeHtml(
- CommonFormat.formatDate((long)lastDinnerData.forwardDate()*1000, 8)
- ), MsgEscape.escapeHtml(
- CommonFormat.formatDuration(System.currentTimeMillis()-(long)lastDinnerData.forwardDate()*1000)
- )
- )
- ).replyToMessageId(sendResp.message().messageId()).parseMode(ParseMode.HTML));
- isAllowed = true;
- } else {
- MornyCoeur.extra().exec(new SendSticker(
- event.message().from().id(),
- TelegramStickers.ID_403
- ).replyToMessageId(event.message().messageId()));
- }
- MornyCoeur.extra().exec(new SendMessage(
- ME, String.format(
- """
- request Last Annie Dinner
- from %s
- %s""",
- TGToString.as(event.message().from()).fullnameRefHtml(),
- isAllowed ? "Allowed and returned " + String.format(
- "https://t.me/c/%d/%d", Math.abs(lastDinnerData.forwardFromChat().id()+1000000000000L), lastDinnerData.forwardFromMessageId()
- ) : "Forbidden by perm check."
- )
- ).parseMode(ParseMode.HTML));
- }
-
- /**
- * 执行自定义呼叫
- * 将会向 {@link #ME} 发送一个 request 数据消息和转发的原始请求消息
- *
- * known issue
${h(_text.text)}
"
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ }
+
+ private def echoHelp(chat: Long, replyTo: Int): Unit =
+ MornyCoeur.extra exec SendMessage(
+ chat,
+ s"""base64, b64
+ |base64url, base64u, b64u
+ |base64decode, base64d, b64d
+ |base64url-decode, base64ud, b64ud
+ |sha1
+ |sha256
+ |sha512
+ |md5
+ |---
+ |uppercase, upper, u (sha1/sha256/sha512/md5 only)"""
+ .stripMargin
+ ).replyToMessageId(replyTo).parseMode(ParseMode HTML)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala
new file mode 100644
index 0000000..a4fddb0
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/EventHack.scala
@@ -0,0 +1,56 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+import OnEventHackHandle.{HackType, registerHack}
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import com.pengrad.telegrambot.request.SendSticker
+
+import scala.language.postfixOps
+
+object EventHack extends ITelegramCommand {
+
+ override def getName: String = "event_hack"
+ override def getAliases: Array[String] = null
+ override def getParamRule: String = "[(user|group|any)]"
+ override def getDescription: String = "输出 bot 下一个获取到的事件序列化数据"
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ val x_mode = if (command.hasArgs) command.getArgs()(0) else ""
+
+ def done_ok =
+ MornyCoeur.extra exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_WAITING
+ ).replyToMessageId(event.message.messageId)
+ def done_forbiddenForAny =
+ MornyCoeur.extra exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(event.message.messageId)
+
+ def doRegister (t: HackType): Unit =
+ registerHack(
+ event.message.messageId longValue,
+ event.message.from.id,
+ event.message.chat.id,
+ t
+ )
+ x_mode match
+ case "any" =>
+ if (MornyCoeur.trustedInstance isTrusted event.message.from.id)
+ doRegister(HackType ANY)
+ done_ok
+ else done_forbiddenForAny
+ case "group" =>
+ doRegister(HackType GROUP)
+ done_ok
+ case _ =>
+ doRegister(HackType USER)
+ done_ok
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala
new file mode 100644
index 0000000..3f03650
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/GetUsernameAndId.scala
@@ -0,0 +1,66 @@
+package cc.sukazyo.cono.morny.bot.command
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
+import cc.sukazyo.cono.morny.util.tgapi.{InputCommand, Standardize}
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{GetChatMember, SendMessage}
+
+import scala.language.postfixOps
+
+object GetUsernameAndId extends ITelegramCommand {
+
+ override def getName: String = "user"
+ override def getAliases: Array[String] = Array()
+ override def getParamRule: String = "[userid]"
+ override def getDescription: String = "获取指定或回复的用户相关信息"
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ val args = command.getArgs
+
+ if (args.length > 1)
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] Too much arguments."
+ ).replyToMessageId(event.message.messageId)
+ return
+
+ val userId: Long =
+ if (args nonEmpty) {
+ try args(0) toLong
+ catch case e: NumberFormatException =>
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ s"[Unavailable] ${e.getMessage}"
+ ).replyToMessageId(event.message.messageId)
+ return
+ } else if (event.message.replyToMessage eq null) event.message.from.id
+ else event.message.replyToMessage.from.id
+
+ val response = MornyCoeur.getAccount execute GetChatMember(event.message.chat.id, userId)
+
+ if (response.chatMember eq null)
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] user not found."
+ ).replyToMessageId(event.message.messageId)
+ return
+
+ val user = response.chatMember.user
+
+ if (user.id eq Standardize.CHANNEL_SPEAKER_MAGIC_ID)
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ "$__channel_identify
"
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ TelegramUserInformation informationOutputHTML user
+ ).replyToMessageId(event.message.messageId()).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala
new file mode 100644
index 0000000..a901580
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/IP186Query.scala
@@ -0,0 +1,77 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+object IP186Query {
+
+ private enum Subs (val cmd: String):
+ case IP extends Subs("ip")
+ case WHOIS extends Subs("whois")
+
+ object IP extends ITelegramCommand:
+ override def getName: String = "ip"
+ override def getAliases: Array[String] = null
+ override def getParamRule: String = "[ip]"
+ override def getDescription: String = "通过 https://ip.186526.xyz 查询 ip 资料"
+ override def execute (command: InputCommand, event: Update): Unit = query(event, command)
+ object Whois extends ITelegramCommand:
+ override def getName: String = "whois"
+ override def getAliases: Array[String] = null
+ override def getParamRule: String = "[domain]"
+ override def getDescription: String = "通过 https://ip.186526.xyz 查询域名资料"
+ override def execute (command: InputCommand, event: Update): Unit = query(event, command)
+
+ private def query (event: Update, command: InputCommand): Unit = {
+
+ val target: String|Null =
+ if (command.getArgs isEmpty)
+ if event.message.replyToMessage eq null then null else event.message.replyToMessage.text
+ else if (command.getArgs.length > 1)
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ "[Unavailable] Too much arguments."
+ ).replyToMessageId(event.message.messageId)
+ return
+ else command.getArgs()(0)
+
+ if (target eq null)
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ "[Unavailable] No ip defined."
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+ try {
+
+ val response = command.getCommand match
+ case Subs.IP.cmd => IP186QueryHandler.query_ip(target)
+ case Subs.WHOIS.cmd => IP186QueryHandler.query_whoisPretty(target)
+ case _ => throw IllegalArgumentException(s"Unknown 186-IP query method ${command.getCommand}")
+
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ s"""${h(response.url)}
+ |${h(response.body)}
"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ } catch case e: Exception =>
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+ MornyCoeur.extra().exec(new SendMessage(
+ event.message().chat().id(),
+ s"""[Exception] in query:
+ |${h(e.getMessage)}
"""
+ .stripMargin
+ ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.scala
new file mode 100644
index 0000000..2df1ba5
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInfoOnHello.scala
@@ -0,0 +1,35 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendPhoto
+
+import scala.language.postfixOps
+
+object MornyInfoOnHello extends ISimpleCommand {
+
+ override def getName: String = "start"
+ override def getAliases: Array[String] = Array()
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ MornyCoeur.extra exec new SendPhoto(
+ event.message.chat.id,
+ MornyInformation.getAboutPic
+ ).caption(
+ s"""欢迎使用 Morny Cono,来自安妮的侍从小鼠。
+ |Morny 具有各种各样的功能。
+ |
+ |————————————————
+ |${MornyInformation.getMornyAboutLinksHTML}
+ |————————————————
+ |
+ |(你可以随时通过 /info 重新获得这些信息)"""
+ .stripMargin
+ ).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala
new file mode 100644
index 0000000..e81a9fb
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/MornyInformation.scala
@@ -0,0 +1,167 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.data.{TelegramImages, TelegramStickers}
+import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.{BuildConfig, MornyAbout, MornyCoeur, MornySystem}
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{SendMessage, SendPhoto, SendSticker}
+
+import java.lang.System
+import java.net.InetAddress
+import java.rmi.UnknownHostException
+import scala.language.postfixOps
+
+object MornyInformation extends ITelegramCommand {
+
+ private case object Subs {
+ val STICKERS = "stickers"
+ val RUNTIME = "runtime"
+ val VERSION = "version"
+ val VERSION_2 = "v"
+ }
+
+ override def getName: String = "info"
+ override def getAliases: Array[String] = Array()
+ override def getParamRule: String = "[(version|runtime|stickers[.IDs])]"
+ override def getDescription: String = "输出当前 Morny 的各种信息"
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ if (!command.hasArgs) {
+ echoInfo(event.message.chat.id, event.message.messageId)
+ return
+ }
+
+ val action: String = command.getArgs()(0)
+
+ action match {
+ case Subs.STICKERS => echoStickers(command, event)
+ case Subs.RUNTIME => echoRuntime(event)
+ case Subs.VERSION | Subs.VERSION_2 => echoVersion(event)
+ case _ => echo404(event)
+ }
+
+ }
+
+ def getVersionGitTagHTML: String = {
+ if (!MornySystem.isGitBuild) return ""
+ val g = StringBuilder()
+ val cm = BuildConfig.COMMIT substring(0, 8)
+ val cp = MornySystem.currentCodePath
+ if (cp == null) g++= s"$cm
"
+ else g++= s"$cm"
+ if (!MornySystem.isCleanBuild) g++= ".δ
"
+ g toString
+ }
+
+ def getVersionAllFullTagHTML: String = {
+ val v = StringBuilder()
+ v ++= s"${MornySystem VERSION_BASE}
"
+ if (MornySystem isUseDelta) v++=s"-δ${MornySystem VERSION_DELTA}
"
+ if (MornySystem isGitBuild) v++="+"++=getVersionGitTagHTML
+ v ++= s"*${MornySystem.CODENAME toUpperCase}
"
+ v toString
+ }
+
+ def getRuntimeHostname: String|Null = {
+ try InetAddress.getLocalHost.getHostName
+ catch case _:UnknownHostException => null
+ }
+
+ def getAboutPic: Array[Byte] = TelegramImages.IMG_ABOUT get
+
+ def getMornyAboutLinksHTML: String =
+ s"""source code | backup
+ |反馈 / issue tracker
+ |使用说明书 / user guide & docs"""
+ .stripMargin
+
+ private def echoInfo (chatId: Long, replyTo: Int): Unit = {
+ MornyCoeur.extra exec new SendPhoto(
+ chatId,
+ getAboutPic
+ ).caption(
+ s"""Morny Cono
+ |来自安妮的侍从小鼠。
+ |————————————————
+ |$getMornyAboutLinksHTML"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(replyTo)
+ }
+
+ private def echoStickers (command: InputCommand, event: Update): Unit = {
+ val chat = event.message.chat.id
+ val replyTo = event.message.messageId
+ var sid: String|Null = null
+ if (command.getArgs()(0) eq Subs.STICKERS) {
+ if (command.getArgs.length == 1) sid = ""
+ else if (command.getArgs.length == 2) sid = command.getArgs()(1)
+ } else if (command.getArgs.length == 1) {
+ if ((command.getArgs()(0) startsWith s"${Subs.STICKERS}.") || (command.getArgs()(0) startsWith s"${Subs.STICKERS}#")) {
+ sid = command.getArgs()(0) substring Subs.STICKERS.length+1
+ }
+ }
+ if (sid == null) echo404(event)
+ else echoStickers(sid, chat, replyTo)
+ }
+
+ private def echoStickers (sid: String, send_chat: Long, send_replyTo: Int): Unit = {
+ if (sid isEmpty) TelegramStickers echoAllStickers(MornyCoeur.extra, send_chat, send_replyTo)
+ else TelegramStickers echoStickerByID(sid, MornyCoeur.extra, send_chat, send_replyTo)
+ }
+
+ private[command] def echoVersion (event: Update): Unit = {
+ val versionDeltaHTML = if (MornySystem.isUseDelta) s"-δ${h(MornySystem.VERSION_DELTA)}
" else ""
+ val versionGitHTML = if (MornySystem.isGitBuild) s"git $getVersionGitTagHTML" else ""
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ s"""version:
+ |- Morny ${h(MornySystem.CODENAME toUpperCase)}
+ |- ${h(MornySystem.VERSION_BASE)}
$versionDeltaHTML${if (MornySystem.isGitBuild) "\n- " + versionGitHTML else ""}
+ |coeur md5_hash:
+ |- ${h(MornySystem.getJarMd5)}
+ |coding timestamp:
+ |- ${BuildConfig.CODE_TIMESTAMP}
+ |- ${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]
+ |""".stripMargin
+ ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
+ }
+
+ private[command] def echoRuntime (event: Update): Unit = {
+ def sysprop (p: String): String = System.getProperty(p)
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ /* html */
+ s"""system:
+ |- Morny ${h(if (getRuntimeHostname == null) "" else getRuntimeHostname)}
+ |- ${h(sysprop("os.name"))}
${h(sysprop("os.arch"))}
${h(sysprop("os.version"))}
+ |java runtime:
+ |- ${h(sysprop("java.vm.vendor"))}.${h(sysprop("java.vm.name"))}
+ |- ${h(sysprop("java.vm.version"))}
+ |vm memory:
+ |- ${Runtime.getRuntime.totalMemory/1024/1024}
/ ${Runtime.getRuntime.maxMemory/1024/1024}
+ |- ${Runtime.getRuntime.availableProcessors}
cores
+ |coeur version:
+ |- $getVersionAllFullTagHTML
+ |- ${h(MornySystem.getJarMd5)}
+ |- ${h(formatDate(BuildConfig.CODE_TIMESTAMP, 0))} [UTC]
+ |- [${BuildConfig.CODE_TIMESTAMP}
]
+ |continuous:
+ |- ${h(formatDuration(System.currentTimeMillis - MornyCoeur.coeurStartTimestamp))}
+ |- [${System.currentTimeMillis - MornyCoeur.coeurStartTimestamp}
]
+ |- ${h(formatDate(MornyCoeur.coeurStartTimestamp, 0))}
+ |- [${MornyCoeur.coeurStartTimestamp}
]"""
+ .stripMargin
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+ }
+
+ private def echo404 (event: Update): Unit =
+ MornyCoeur.extra exec new SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(event.message.messageId)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala
new file mode 100644
index 0000000..5c7d799
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Nbnhhsh.scala
@@ -0,0 +1,85 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.{NbnhhshQuery, TelegramStickers}
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
+
+import java.io.IOException
+import scala.language.postfixOps
+
+object Nbnhhsh extends ITelegramCommand {
+
+ private val NBNHHSH_RESULT_HEAD_HTML = "## Result of nbnhhsh query :"
+
+ override def getName: String = "nbnhhsh"
+ override def getAliases: Array[String]|Null = null
+ override def getParamRule: String = "[text]"
+ override def getDescription: String = "检索文本内 nbnhhsh 词条"
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ val queryTarget: String|Null =
+ import cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting
+ if (event.message.replyToMessage != null && event.message.replyToMessage.text != null)
+ event.message.replyToMessage.text
+ else if command hasArgs then
+ stringsConnecting(command.getArgs, " ", 0, command.getArgs.length-1)
+ else null
+
+ if (queryTarget == null)
+ MornyCoeur.extra exec SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_404
+ ).replyToMessageId(event.message.messageId)
+ return;
+
+ try {
+
+ val queryResp = NbnhhshQuery sendGuess queryTarget
+
+ val message = StringBuilder(NBNHHSH_RESULT_HEAD_HTML)
+
+ import cc.sukazyo.cono.morny.Log.logger
+ logger debug s"**xx len=${queryResp.words.length}"
+ for (_word <- queryResp.words) {
+ logger debug "**exec"
+ if ((_word.trans ne null) && (_word.trans isEmpty)) _word.trans = null
+ if ((_word.inputting ne null) && (_word.inputting isEmpty)) _word.inputting = null
+ if ((_word.trans ne null) || (_word.inputting ne null))
+ message ++= s"\n\n[[ ${h(_word.name)} ]]"
+ logger debug s"**used [${_word.name}]"
+ if (_word.trans != null) for (_trans <- _word.trans)
+ message ++= s"\n* ${h(_trans)}"
+ logger debug s"**used [${_word.name}] used `${_trans}``"
+ if (_word.inputting != null)
+ logger debug s"**used [${_word.name}] inputting"
+ if (_word.trans != null)
+ message += '\n'
+ message ++= " maybe:"
+ for (_inputting <- _word.inputting)
+ logger debug s"**used [${_word.name}] used-i ${_inputting}"
+ message ++= s"\n` ${h(_inputting)}"
+ logger debug s"**exec as ${_word.name}"
+ }
+
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ message toString
+ ).parseMode(ParseMode HTML).replyToMessageId(event.message.messageId)
+
+ } catch case e: IOException => {
+ MornyCoeur.extra exec SendMessage(
+ event.message.chat.id,
+ s"""[Exception] in query:
+ |${h(e.getMessage)}
+ |""".stripMargin
+ )
+ }
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala
new file mode 100644
index 0000000..4d453da
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/Testing.scala
@@ -0,0 +1,30 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import javax.annotation.Nonnull
+import javax.annotation.Nullable
+import scala.language.postfixOps
+
+object Testing extends ISimpleCommand {
+
+ override def getName: String = "test"
+ override def getAliases: Array[String] = null
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ val a = StringBuilder("value")
+ a ++= "Changed"
+
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ "Just a TEST command. num is:" + (a toString)
+ ).replyToMessageId(event.message.messageId).parseMode(ParseMode HTML)
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala
new file mode 100644
index 0000000..c870e8e
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/喵呜.scala
@@ -0,0 +1,67 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.model.{Message, Update}
+import com.pengrad.telegrambot.request.{SendMessage, SendSticker}
+
+import javax.swing.text.html.HTML
+import scala.annotation.unused
+import scala.language.postfixOps
+
+@SuppressWarnings(Array("NonAsciiCharacters"))
+object 喵呜 {
+
+ object 抱抱 extends ISimpleCommand {
+ override def getName: String = "抱抱"
+ override def getAliases: Array[String] = Array()
+ override def execute (command: InputCommand, event: Update): Unit =
+ replyingSet(event, "贴贴", "贴贴")
+ }
+
+ object 揉揉 extends ISimpleCommand {
+ override def getName: String = "揉揉"
+ override def getAliases: Array[String] = Array()
+ override def execute (command: InputCommand, event: Update): Unit =
+ replyingSet(event, "蹭蹭", "摸摸")
+ }
+
+ object 蹭蹭 extends ISimpleCommand {
+ override def getName: String = "蹭蹭"
+ override def getAliases: Array[String] = Array()
+ override def execute (command: InputCommand, event: Update): Unit =
+ replyingSet(event, "揉揉", "蹭蹭")
+ }
+
+ object 贴贴 extends ISimpleCommand {
+ override def getName: String = "贴贴"
+ override def getAliases: Array[String] = Array()
+ override def execute (command: InputCommand, event: Update): Unit =
+ replyingSet(event, "贴贴", "贴贴")
+ }
+
+ object Progynova extends ITelegramCommand {
+ override def getName: String = "install"
+ override def getAliases: Array[String] = Array()
+ override def getParamRule: String = ""
+ override def getDescription: String = "抽取一个神秘盒子"
+ override def execute (command: InputCommand, event: Update): Unit = {
+ MornyCoeur.extra exec new SendSticker(
+ event.message.chat.id,
+ TelegramStickers ID_PROGYNOVA
+ ).replyToMessageId(event.message.messageId)
+ }
+ }
+
+ private def replyingSet (event: Update, whileRec: String, whileNew: String): Unit = {
+ val isNew = event.message.replyToMessage == null;
+ val target = if (isNew) event.message else event.message.replyToMessage
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ if (isNew) whileNew else whileRec
+ ).replyToMessageId(target.messageId).parseMode(ParseMode HTML)
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala
new file mode 100644
index 0000000..67da3f5
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/command/私わね.scala
@@ -0,0 +1,26 @@
+package cc.sukazyo.cono.morny.bot.command
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.util.tgapi.InputCommand
+import cc.sukazyo.cono.morny.util.CommonRandom.probabilityTrue
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendMessage
+
+object 私わね extends ISimpleCommand {
+
+ override def getName: String = "me"
+ override def getAliases: Array[String] = Array()
+
+ override def execute (command: InputCommand, event: Update): Unit = {
+
+ if (probabilityTrue(521)) {
+ val text = "/打假"
+ MornyCoeur.extra exec new SendMessage(
+ event.message.chat.id,
+ text
+ ).replyToMessageId(event.message.messageId)
+ }
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
new file mode 100644
index 0000000..1f51e4d
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnCallMe.scala
@@ -0,0 +1,90 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import cc.sukazyo.cono.morny.data.TelegramStickers
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.model.{Chat, Message, Update, User}
+import com.pengrad.telegrambot.request.{ForwardMessage, GetChat, SendMessage, SendSticker}
+
+import scala.language.postfixOps
+
+object OnCallMe extends EventListener {
+
+ private val me = MornyCoeur.config.trustedMaster
+
+ override def onMessage (update: Update): Boolean = {
+
+ if update.message.text == null then return false
+ if update.message.chat.`type` != (Chat.Type Private) then return false
+
+ (update.message.text toLowerCase) match
+ case "steam" | "sbeam" | "sdeam" =>
+ requestItem(update.message.from, "STEAM LIBRARY")
+ case "hana paresu" | "花宫" | "内群" =>
+ requestItem(update.message.from, "Hana Paresu")
+ case "dinner" | "lunch" | "breakfast" | "meal" | "eating" | "安妮今天吃什么" =>
+ requestLastDinner(update.message)
+ case cc if cc startsWith "cc::" =>
+ requestCustom(update.message)
+ case _ =>
+ return false
+
+ MornyCoeur.extra exec SendSticker(
+ update.message.chat.id,
+ TelegramStickers ID_SENT
+ ).replyToMessageId(update.message.messageId)
+ true
+
+ }
+
+ private def requestItem (user: User, itemHTML: String, extra: String|Null = null): Unit =
+ MornyCoeur.extra exec SendMessage(
+ me,
+ s"""request $itemHTML
+ |from ${(TGToString as user) fullnameRefHtml}${if extra == null then "" else "\n"+extra}"""
+ .stripMargin
+ ).parseMode(ParseMode HTML)
+
+ private def requestLastDinner (req: Message): Unit = {
+ var isAllowed = false
+ var lastDinnerData: Message|Null = null
+ if (MornyCoeur.trustedInstance isTrustedForDinnerRead req.from.id) {
+ lastDinnerData = (MornyCoeur.extra exec GetChat(MornyCoeur.config.dinnerChatId)).chat.pinnedMessage
+ val sendResp = MornyCoeur.extra exec ForwardMessage(
+ req.from.id,
+ lastDinnerData.forwardFromChat.id,
+ lastDinnerData.forwardFromMessageId
+ )
+ import cc.sukazyo.cono.morny.util.CommonFormat.{formatDate, formatDuration}
+ import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+ def lastDinner_dateMillis: Long = lastDinnerData.forwardDate longValue;
+ MornyCoeur.extra exec SendMessage(
+ req.from.id,
+ "on %s [UTC+8]
\n- %s
before".formatted(
+ h(formatDate(lastDinner_dateMillis, 8)),
+ h(formatDuration(lastDinner_dateMillis))
+ )
+ ).parseMode(ParseMode HTML).replyToMessageId(sendResp.message.messageId)
+ isAllowed = true
+ } else {
+ MornyCoeur.extra exec SendSticker(
+ req.from.id,
+ TelegramStickers ID_403
+ ).replyToMessageId(req.messageId)
+ }
+ import Math.abs
+ requestItem(
+ req.from, "Last Annie Dinner",
+ if isAllowed then s"Allowed and returned https://t.me/c/${abs(lastDinnerData.forwardFromChat.id+1000000000000L)}/${lastDinnerData.forwardFromMessageId}"
+ else "Forbidden by perm check."
+ )
+ }
+
+ private def requestCustom (message: Message): Unit =
+ requestItem(message.from, "[???]")
+ MornyCoeur.extra exec ForwardMessage(me, message.chat.id, message.messageId)
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala
new file mode 100644
index 0000000..96f2ad0
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnInlineQuery.scala
@@ -0,0 +1,37 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.api.{EventListener, InlineQueryUnit}
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.InlineQueryResult
+import com.pengrad.telegrambot.request.AnswerInlineQuery
+
+import scala.collection.mutable.ListBuffer
+import scala.language.postfixOps
+import scala.reflect.ClassTag
+
+object OnInlineQuery extends EventListener {
+
+ override def onInlineQuery (update: Update): Boolean = {
+
+ val results: List[InlineQueryUnit[_]] = MornyCoeur.queryManager query update
+
+ var cacheTime = Int.MaxValue
+ var isPersonal = InlineQueryUnit.DEFAULT_INLINE_PERSONAL_RESP
+ val resultAnswers = ListBuffer[InlineQueryResult[_]]()
+ for (r <- results) {
+ if (cacheTime > r.cacheTime) cacheTime = r.cacheTime
+ if (r isPersonal) isPersonal = true
+ resultAnswers += r.result
+ }
+
+ if (results isEmpty) return false
+
+ MornyCoeur.extra exec AnswerInlineQuery(
+ update.inlineQuery.id, resultAnswers toArray:_*
+ ).cacheTime(cacheTime).isPersonal(isPersonal)
+ true
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala
new file mode 100644
index 0000000..2e21f63
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserRandom.scala
@@ -0,0 +1,38 @@
+package cc.sukazyo.cono.morny.bot.event
+
+import cc.sukazyo.cono.morny.MornyCoeur
+import cc.sukazyo.cono.morny.bot.api.EventListener
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+object OnUserRandom extends EventListener {
+
+ private val USER_OR_QUERY = "(.+)(?:还是|or)(.+)"r
+ private val USER_IF_QUERY = "(.+)[吗?|?]+$"r
+
+ override def onMessage(update: Update): Boolean = {
+
+ if update.message.text == null then return false
+ if update.message.text startsWith "/" then return false
+
+ import cc.sukazyo.cono.morny.util.CommonRandom.iif
+ val query = update.message.text substring 1
+ val result: String|Null = query match
+ case USER_OR_QUERY(_con1, _con2) =>
+ if iif then _con1 else _con2
+ case USER_IF_QUERY(_con) =>
+ (if iif then "不" else "") + _con
+ case _ => null
+
+ if result == null then return false
+
+ MornyCoeur.extra exec SendMessage(
+ update.message.chat.id, result
+ ).replyToMessageId(update.message.messageId)
+ true
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala
new file mode 100644
index 0000000..1970726
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.scala
@@ -0,0 +1,67 @@
+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.UniversalCommand
+import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml as h
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.ParseMode
+import com.pengrad.telegrambot.request.SendMessage
+
+import scala.language.postfixOps
+
+object OnUserSlashAction extends EventListener {
+
+ private val TG_FORMAT = "^\\w+(@\\w+)?$"r
+
+ override def onMessage (update: Update): Boolean = {
+
+ val text = update.message.text;
+ if text == null then return false
+
+ if (text startsWith "/") {
+
+ val actions = UniversalCommand format text
+ actions(0) = actions(0) substring 1
+
+ actions(0)
+
+ actions(0) match
+ case TG_FORMAT(_) =>
+ return false
+ case x if x contains "/" => return false
+
+ val isHardParse = actions(0) isBlank
+ def hp_len(i: Int) = if isHardParse then i+1 else i
+ if isHardParse && actions.length < 2 then return false
+ val v_verb = actions(hp_len(0))
+ val hasObject = actions.length != hp_len(1)
+ val v_object =
+ if hasObject then
+ actions slice(hp_len(1), actions.length) mkString(" ")
+ else ""
+ val origin = update.message
+ val target =
+ if update.message.replyToMessage == null then
+ origin
+ else update.message.replyToMessage
+
+ MornyCoeur.extra exec SendMessage(
+ update.message.chat.id,
+ "%s %s%s %s %s!".format(
+ (TGToString as origin) getSenderFirstNameRefHtml,
+ h(v_verb), if hasObject then "" else "了",
+ if (origin == target)
+ s"自己"
+ else (TGToString as target) getSenderFirstNameRefHtml,
+ if hasObject then h(v_object+" ") else ""
+ )
+ ).parseMode(ParseMode HTML).replyToMessageId(update.message.messageId)
+ true
+
+ } else false
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala
new file mode 100644
index 0000000..d03a05c
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ITelegramQuery.scala
@@ -0,0 +1,11 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import javax.annotation.Nullable
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import com.pengrad.telegrambot.model.Update
+
+trait ITelegramQuery {
+
+ def query (event: Update): List[InlineQueryUnit[_]] | Null
+
+}
\ No newline at end of file
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala
new file mode 100644
index 0000000..faeaefb
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MornyQueries.scala
@@ -0,0 +1,27 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import cc.sukazyo.cono.morny.bot.query
+import com.pengrad.telegrambot.model.Update
+
+import scala.collection.mutable.ListBuffer
+
+class MornyQueries {
+
+ private val queryInstances = Set[ITelegramQuery](
+ RawText,
+ MyInformation,
+ ShareToolTwitter,
+ ShareToolBilibili
+ )
+
+ def query (event: Update): List[InlineQueryUnit[_]] = {
+ val results = ListBuffer[InlineQueryUnit[_]]()
+ for (instance <- queryInstances) {
+ val r = instance query event
+ if (r != null) results ++= r
+ }
+ results.result()
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala
new file mode 100644
index 0000000..c98feb6
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/MyInformation.scala
@@ -0,0 +1,31 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
+import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode}
+
+import scala.language.postfixOps
+
+object MyInformation extends ITelegramQuery {
+
+ private val ID_PREFIX = "[morny/info/me]"
+ private val TITLE = "My Account Information"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null || (event.inlineQuery.query isBlank)) return null
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX), TITLE,
+ new InputTextMessageContent(
+ TelegramUserInformation informationOutputHTML event.inlineQuery.from
+ ).parseMode(ParseMode HTML)
+ )).isPersonal(true).cacheTime(10)
+ )
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala
new file mode 100644
index 0000000..2d19149
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/RawText.scala
@@ -0,0 +1,27 @@
+package cc.sukazyo.cono.morny.bot.query
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent}
+
+import scala.language.postfixOps
+
+object RawText extends ITelegramQuery {
+
+ private val ID_PREFIX = "[morny/r/text]"
+ private val TITLE = "Raw Text"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null || (event.inlineQuery.query isBlank)) return null
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX, event.inlineQuery.query), TITLE,
+ InputTextMessageContent(event.inlineQuery.query)
+ ))
+ )
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala
new file mode 100644
index 0000000..90288e2
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolBilibili.scala
@@ -0,0 +1,77 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.Log.logger
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import cc.sukazyo.cono.morny.util.BiliTool
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.{InlineQueryResultArticle, InputTextMessageContent, ParseMode}
+
+import scala.language.postfixOps
+import scala.util.matching.Regex
+
+object ShareToolBilibili extends ITelegramQuery {
+
+ private val TITLE_BILI_AV = "[bilibili] Share video / av"
+ private val TITLE_BILI_BV = "[bilibili] Share video / BV"
+ private val ID_PREFIX_BILI_AV = "[morny/share/bili/av]"
+ private val ID_PREFIX_BILI_BV = "[morny/share/bili/bv]"
+ private val LINK_PREFIX = "https://bilibili.com/video/"
+ private val REGEX_BILI_VIDEO: Regex = "^(?:(?:https?://)?(?:www\\.)?bilibili\\.com(?:/s)?/video/((?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))/?(\\?(?:p=(\\d+))?.*)?|(?:av|AV)(\\d{1,12})|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]{10}))$"r
+ private val SHARE_FORMAT_HTML = "%s"
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null) return null
+
+ event.inlineQuery.query match
+ case REGEX_BILI_VIDEO(_1, _2, _3, _4, _5, _6, _7) =>
+
+ logger debug
+ s"""====== Share Tool Bilibili Catch ok
+ |1: ${_1}
+ |2: ${_2}
+ |3: ${_3}
+ |4: ${_4}
+ |5: ${_5}
+ |6: ${_6}
+ |7: ${_7}"""
+ .stripMargin
+
+ var av = if (_2 != null) _2 else if (_6 != null) _6 else null
+ var bv = if (_3!=null) _3 else if (_7!=null) _7 else null
+ logger trace s"catch id av[$av] bv[$bv]"
+ val part: Int|Null = if (_5!=null) _5 toInt else null
+ logger trace s"catch video part[$part]"
+
+ if (av == null) {
+ assert (bv != null)
+ av = BiliTool.toAv(bv) toString;
+ logger trace s"converted bv[$av] to av[$av]"
+ } else {
+ bv = BiliTool.toBv(av toLong)
+ logger trace s"converted av[$av] to bv[$bv]"
+ }
+
+ val id_av = s"av$av"
+ val id_bv = s"BV$bv"
+ val linkParams = if (part!=null) s"?p=$part" else ""
+ val link_av = LINK_PREFIX + id_av + linkParams
+ val link_bv = LINK_PREFIX + id_bv + linkParams
+
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX_BILI_AV+av), TITLE_BILI_AV+av,
+ InputTextMessageContent(SHARE_FORMAT_HTML.format(link_av, id_av)).parseMode(ParseMode HTML)
+ )),
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX_BILI_BV + bv), TITLE_BILI_BV + bv,
+ InputTextMessageContent(SHARE_FORMAT_HTML.format(link_bv, id_bv)).parseMode(ParseMode HTML)
+ ))
+ )
+
+ case _ => null
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala
new file mode 100644
index 0000000..9427dca
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/bot/query/ShareToolTwitter.scala
@@ -0,0 +1,42 @@
+package cc.sukazyo.cono.morny.bot.query
+
+import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit
+import com.pengrad.telegrambot.model.Update
+import com.pengrad.telegrambot.model.request.InlineQueryResultArticle
+
+import cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds
+
+import scala.language.postfixOps
+import scala.util.matching.Regex
+
+object ShareToolTwitter extends ITelegramQuery {
+
+ val TITLE_VX = "[tweet] Share as VxTwitter"
+ val TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)"
+ val ID_PREFIX_VX = "[morny/share/twitter/vxtwi]"
+ val ID_PREFIX_VX_COMBINED = "[morny/share/twitter/vxtwi_combine]"
+ val REGEX_TWEET_LINK: Regex = "^(?:https?://)?((?:(?:c\\.)?vx|fx|www\\.)?twitter\\.com)/((\\w+)/status/(\\d+)(?:/photo/(\\d+))?)/?(\\?[\\w&=-]+)?$"r
+
+ override def query (event: Update): List[InlineQueryUnit[_]] | Null = {
+
+ if (event.inlineQuery.query == null) return null
+
+ event.inlineQuery.query match
+
+ case REGEX_TWEET_LINK(_1, _2, _) =>
+ List(
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX_VX+event.inlineQuery.query), TITLE_VX,
+ s"https://vxtwitter.com/$_2"
+ )),
+ InlineQueryUnit(InlineQueryResultArticle(
+ inlineIds(ID_PREFIX_VX_COMBINED+event.inlineQuery.query), TITLE_VX_COMBINED,
+ s"https://c.vxtwitter.com/$_2"
+ ))
+ )
+
+ case _ => null
+
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.scala b/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.scala
new file mode 100644
index 0000000..65abaca
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186QueryHandler.scala
@@ -0,0 +1,41 @@
+package cc.sukazyo.cono.morny.data.ip186
+
+import okhttp3.{OkHttpClient, Request}
+
+import java.io.IOException
+import scala.language.postfixOps
+import scala.util.Using
+
+object IP186QueryHandler {
+
+ private val SITE_URL = "https://ip.186526.xyz/"
+ private val QUERY_PARAM_IP = "type=json&format=true"
+ private val QUERY_PARAM_WHOIS = "type=plain"
+
+ private val httpClient = OkHttpClient()
+
+ @throws[IOException]
+ def query_ip (ip: String): IP186Response =
+ commonQuery(SITE_URL + ip, QUERY_PARAM_IP)
+
+ @throws[IOException]
+ def query_whois (domain: String): IP186Response =
+ commonQuery(SITE_URL+"whois/"+domain, QUERY_PARAM_WHOIS)
+
+ @throws[IOException]
+ def query_whoisPretty (domain: String): IP186Response =
+ val raw = query_whois(domain)
+ IP186Response(raw.url, raw.body substring(0, (raw.body indexOf "<<<")+3))
+
+ @throws[IOException]
+ private def commonQuery (requestUrl: String, queryParam: String): IP186Response = {
+ val request = Request.Builder().url(requestUrl + "?" + queryParam).build
+ var _body_string: String|Null = null
+ Using ((httpClient newCall request) execute) { response =>
+ if response.body ne null then _body_string = response.body.string
+ }
+ if _body_string eq null then throw IOException("Response of ip186: body is empty!")
+ IP186Response(requestUrl, _body_string)
+ }
+
+}
diff --git a/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186Response.scala b/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186Response.scala
new file mode 100644
index 0000000..6fcad79
--- /dev/null
+++ b/src/main/scala/cc/sukazyo/cono/morny/data/ip186/IP186Response.scala
@@ -0,0 +1,3 @@
+package cc.sukazyo.cono.morny.data.ip186
+
+case class IP186Response (url: String, body: String)