diff --git a/build.gradle b/build.gradle index 60a856a..21ab1a4 100644 --- a/build.gradle +++ b/build.gradle @@ -14,7 +14,9 @@ plugins { } final boolean proj_git = grgit!=null +final String proj_store = MORNY_CODE_STORE final String proj_commit = proj_git ? grgit.head().id : null +final String proj_commit_path = MORNY_COMMIT_PATH final boolean proj_clean = isCleanBuild() if (!proj_git) print "[MornyBuild] git repository not available for current working space! git version tag will be disabled." @@ -120,11 +122,14 @@ buildConfig { packageName(proj_package) buildConfigField('String', 'VERSION', "\"${proj_version}\"") + buildConfigField('String', 'VERSION_BASE', "\"${proj_version_base}\"") buildConfigField('String', 'VERSION_DELTA', proj_version_use_delta ? "\"${proj_version_delta}\"" : "null") buildConfigField('String', 'CODENAME', "\"${proj_version_codename}\"") buildConfigField('long', 'CODE_TIMESTAMP', "${proj_code_time}L") buildConfigField('String', 'COMMIT', proj_git ? "\"${proj_commit}\"" : "null") buildConfigField('boolean', 'CLEAN_BUILD', "${proj_clean}") + buildConfigField('String', 'CODE_STORE', proj_store==""?"null":"\"${proj_store}\"") + buildConfigField('String', 'COMMIT_PATH', proj_commit_path==""?"null":"\"${proj_commit_path}\"") } diff --git a/gradle.properties b/gradle.properties index 3db275d..ec523fe 100644 --- a/gradle.properties +++ b/gradle.properties @@ -2,7 +2,10 @@ MORNY_ARCHIVE_NAME = morny-coeur -VERSION = 1.0.0-alpha3 +MORNY_CODE_STORE = https://github.com/Eyre-S/Coeur-Morny-Cono +MORNY_COMMIT_PATH = https://github.com/Eyre-S/Coeur-Morny-Cono/commit/%s + +VERSION = 1.0.0-alpha4 USE_DELTA = false VERSION_DELTA = @@ -15,6 +18,6 @@ libSpotbugsVersion = 4.7.3 libMessivaVersion = 0.1.0.1 -libJavaTelegramBotApiVersion = 5.6.0 +libJavaTelegramBotApiVersion = 6.2.0 libJunitVersion = 5.9.0 diff --git a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java index a62290e..e15a81e 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java @@ -1,8 +1,10 @@ package cc.sukazyo.cono.morny; +import cc.sukazyo.cono.morny.util.BuildConfigField; import cc.sukazyo.cono.morny.util.FileUtils; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.io.IOException; import java.net.URISyntaxException; import java.security.NoSuchAlgorithmException; @@ -13,20 +15,93 @@ import java.security.NoSuchAlgorithmException; public class MornySystem { /** - * 程序的语义化版本号
- * 会由 gradle 任务 {@code updateVersionCode} 更新 + * 程序的语义化版本号. + *

+ * 这个版本号是完整(verbose)构建的——它包含了以下的 {@link #VERSION_BASE}, {@link #VERSION_DELTA}, + * 和简写的 {@link BuildConfig#COMMIT} 字段. + * @since 1.0.0-alpha4 */ - public static final String VERSION = BuildConfig.VERSION; + @BuildConfigField @Nonnull public static final String VERSION = BuildConfig.VERSION; + /** + * 程序的基础版本号. + *

+ * 它只包含了版本号中的主要信息:例如 {@code 0.8.0.5}, {@code 1.0.0-alpha-3}, + * 而不会有用于精确定义的 {@link #VERSION_DELTA} 字段和作为附加使用的 {@link BuildConfig#COMMIT git commit 信息} + * @since 1.0.0-alpha4 + */ + @BuildConfigField @Nonnull public static final String VERSION_BASE = BuildConfig.VERSION_BASE; + /** + * 程序的版本 delta. + * —— 设计上用于在一个基版本当中分出不同构建的版本. + *

+ * {@link null} 作为值,表示这个字段没有被使用. + *

+ * 版本 delta 会以 {@code -δversion-delta} 的形式附着在 {@link #VERSION_BASE} 之后. + *

+ * 目前并不多被使用. + * @since 1.0.0-alpha4 + */ + @BuildConfigField @Nullable public static final String VERSION_DELTA = BuildConfig.VERSION_DELTA; /** - * Morny Coeur 当前的版本代号.
+ * Morny Coeur 当前的版本代号. + *

* 一个单个单词,一般作为一个大版本的名称,只在重大更新改变
* 格式保持为仅由小写字母和数字组成
* 有时也可能是复合词或特殊的词句
*
- * 会由 gradle 任务 {@code updateVersionCode} 更新 */ - public static final String CODENAME = BuildConfig.CODENAME; + @BuildConfigField @Nonnull public static final String CODENAME = BuildConfig.CODENAME; + + /** + * Coeur 的代码仓库的链接. 它应该链接到当前程序的源码主页. + *

+ * {@link null} 表示这个属性在构建时未被设置(或没有源码主页) + * @since 1.0.0-alpha4 + */ + @BuildConfigField @Nullable public static final String CODE_STORE = BuildConfig.CODE_STORE; + /** + * Coeur 的 git commit 链接. + *

+ * 它应该是一个可以通过 {@link String#format(String, Object...)} 要求格式的链接模板,带有一个 {@link String} 类型的槽位 —— + * 通过 String.format(COMMIT_PATH, {@link BuildConfig#COMMIT}) 即可取得当前当前程序所基于的 commit 的链接。 + * @since 1.0.0-alpha4 + */ + @BuildConfigField @Nullable public static final String COMMIT_PATH = BuildConfig.COMMIT_PATH; + + /** @see #VERSION_DELTA */ + @BuildConfigField + public static boolean isUseDelta () { return VERSION_DELTA != null; } + + /** @see BuildConfig#COMMIT */ + @BuildConfigField + @SuppressWarnings("ConstantConditions") + public static boolean isGitBuild () { return BuildConfig.COMMIT != null; } + + /** @see BuildConfig#COMMIT */ + @BuildConfigField + public static boolean isCleanBuild () { return BuildConfig.CLEAN_BUILD; } + + /** + * 获取程序的当前构建所基于的 git commit 的链接. + *

+ * 如果 {@link #COMMIT_PATH}(一般表示没有公开储存库) + * 或是 {@link BuildConfig#COMMIT}(一般表示程序的构建环境没有使用 git) + * 任何一个不可用,则此方法也不可用。 + * + * @return 当前构建的 git commit 链接,为空则表示不可用。 + * @see #COMMIT_PATH + * @since 1.0.0-alpha4 + */ + @Nullable + @BuildConfigField + @SuppressWarnings("ConstantConditions") + public static String currentCodePath () { + if (COMMIT_PATH == null || !isGitBuild()) return null; + return String.format(COMMIT_PATH, BuildConfig.COMMIT); + } + + /** * 获取程序 jar 文件的 md5-hash 值
diff --git a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java index 6c720f8..d7c4b3a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/ServerMain.java +++ b/src/main/java/cc/sukazyo/cono/morny/ServerMain.java @@ -184,14 +184,24 @@ public class ServerMain { logger.info(String.format(""" Morny Cono Version - version : - %s %s + Morny %s + %s%s - md5hash : %s + - gitstat : + %s - co.time : %d %s [UTC]""", - MornySystem.VERSION, MornySystem.CODENAME.toUpperCase(), + MornySystem.CODENAME.toUpperCase(), + MornySystem.VERSION_BASE, + MornySystem.isUseDelta() ? "-δ"+MornySystem.VERSION_DELTA : "", MornySystem.getJarMd5(), + MornySystem.isGitBuild() ? (String.format( + "on commit %s\n %s", + MornySystem.isCleanBuild() ? "- clean-build" : "<δ/non-clean-build>", + BuildConfig.COMMIT + )) : "", BuildConfig.CODE_TIMESTAMP, CommonFormat.formatDate(BuildConfig.CODE_TIMESTAMP, 0) )); @@ -204,11 +214,12 @@ public class ServerMain { logger.info(String.format(""" ServerMain.java Loaded >>> - - version %s (%s)(%d) - - Morny %s""", + - version %s + - Morny %s + - <%s> [%d]""", MornySystem.VERSION, - MornySystem.getJarMd5(), BuildConfig.CODE_TIMESTAMP, - MornySystem.CODENAME.toUpperCase() + MornySystem.CODENAME.toUpperCase(), + MornySystem.getJarMd5(), BuildConfig.CODE_TIMESTAMP )); //# diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java index 8b6f124..f5401fc 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyCommands.java @@ -21,10 +21,7 @@ import javax.annotation.Nullable; import java.net.InetAddress; import java.net.UnknownHostException; -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; +import java.util.*; import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.cono.morny.util.CommonFormat.formatDate; @@ -226,28 +223,33 @@ public class MornyCommands { } } - private static class Version implements ITelegramCommand { + private static class Version implements ISimpleCommand { @Nonnull @Override public String getName () { return "version"; } @Nullable @Override public String[] getAliases () { return null; } - @Nonnull @Override public String getParamRule () { return ""; } - @Nonnull @Override public String getDescription () { return "检查 Bot 版本信息"; } + @Nonnull @Deprecated public String getParamRule () { return ""; } + @Nonnull @Deprecated public String getDescription () { return "检查 Bot 版本信息"; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandVersionExec(event); } } - private static void onCommandVersionExec (@Nonnull Update event) { + public static void onCommandVersionExec (@Nonnull Update event) { MornyCoeur.extra().exec(new SendMessage( event.message().chat().id(), String.format( """ version: - Morny %s - - %s + - %s%s%s core md5_hash: - %s coding timestamp: - %d - %s [UTC]""", escapeHtml(MornySystem.CODENAME.toUpperCase()), - escapeHtml(MornySystem.VERSION), + escapeHtml(MornySystem.VERSION_BASE), + MornySystem.isUseDelta() ? String.format("-δ%s", escapeHtml(Objects.requireNonNull(MornySystem.VERSION_DELTA))) : "", + MornySystem.isGitBuild()?"\n- git "+ (MornySystem.currentCodePath()==null ? + String.format("%s", BuildConfig.COMMIT.substring(0, 8)) : + String.format("%s", MornySystem.currentCodePath(), BuildConfig.COMMIT.substring(0, 8)) + ) + (MornySystem.isCleanBuild() ? "" : ".δ") : "", escapeHtml(MornySystem.getJarMd5()), BuildConfig.CODE_TIMESTAMP, escapeHtml(formatDate(BuildConfig.CODE_TIMESTAMP, 0)) @@ -255,17 +257,17 @@ public class MornyCommands { ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); } - private static class MornyRuntime implements ITelegramCommand { + private static class MornyRuntime implements ISimpleCommand { @Nonnull @Override public String getName () { return "runtime"; } @Nullable @Override public String[] getAliases () { return null; } - @Nonnull @Override public String getParamRule () { return ""; } - @Nonnull @Override public String getDescription () { return "获取 Bot 运行时信息(包括版本号)"; } + @Nonnull @Deprecated public String getParamRule () { return ""; } + @Nonnull @Deprecated public String getDescription () { return "获取 Bot 运行时信息(包括版本号)"; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { onCommandRuntimeExec(event); } } /** * @since 0.4.1.2 */ - private static void onCommandRuntimeExec (@Nonnull Update event) { + public static void onCommandRuntimeExec (@Nonnull Update event) { String hostname; try { hostname = InetAddress.getLocalHost().getHostName(); @@ -286,7 +288,7 @@ public class MornyCommands { - %d / %d MB - %d cores coeur version: - - %s (%s) + - %s%s%s*%s - %s - %s [UTC] - [%d] @@ -307,8 +309,14 @@ public class MornyCommands { Runtime.getRuntime().maxMemory() / 1024 / 1024, Runtime.getRuntime().availableProcessors(), // version - escapeHtml(MornySystem.VERSION), - escapeHtml(MornySystem.CODENAME), + escapeHtml(MornySystem.VERSION_BASE), + MornySystem.isUseDelta() ? String.format("-δ%s", escapeHtml(Objects.requireNonNull(MornySystem.VERSION_DELTA))) : "", + MornySystem.isGitBuild()?String.format("-git.%s%s", MornySystem.currentCodePath()==null ? ( + String.format("%s", BuildConfig.COMMIT.substring(0, 8)) + ) : ( + String.format("%s", MornySystem.currentCodePath(), BuildConfig.COMMIT.substring(0, 8)) + ), MornySystem.isCleanBuild()?"":".δ"):"", + escapeHtml(MornySystem.CODENAME.toUpperCase()), escapeHtml(MornySystem.getJarMd5()), escapeHtml(formatDate(BuildConfig.CODE_TIMESTAMP, 0)), BuildConfig.CODE_TIMESTAMP, diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java index 9d815bb..271f644 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/MornyInformations.java @@ -2,6 +2,7 @@ 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.ExtraAction; import cc.sukazyo.cono.morny.util.tgapi.InputCommand; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.request.SendSticker; @@ -11,36 +12,97 @@ import javax.annotation.Nullable; public class MornyInformations implements ITelegramCommand { - private static final String ACT_STICKER = "stickers"; + 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 "[(stickers)|(stickers.)sticker_id]"; } - @Nonnull @Override public String getDescription () { return "输出 Morny 当前版本的一些预定义信息"; } + @Nonnull @Override public String getParamRule () { return "[subcommand]"; } + @Nonnull @Override public String getDescription () { return "输出当前 Morny 的各种信息"; } @Override public void execute (@Nonnull InputCommand command, @Nonnull Update event) { - if (!command.hasArgs() || command.getArgs().length > 1) { - MornyCoeur.extra().exec(new SendSticker(event.message().chat().id(), TelegramStickers.ID_404).replyToMessageId(event.message().messageId())); - } + if (!command.hasArgs()) echoRuntime(command, event); final String action = command.getArgs()[0]; - if (action.startsWith("stickers")) { - if (action.equals("stickers")) - TelegramStickers.echoAllStickers(MornyCoeur.extra(), event.message().chat().id(), event.message().messageId()); - else { - TelegramStickers.echoStickerByID( - action.substring((ACT_STICKER+".").length()), - MornyCoeur.extra(), event.message().chat().id(), event.message().messageId() - ); - } - return; + if (action.startsWith(SUB_STICKER)) { + echoStickers(command, event); + } else if (action.equals(SUB_RUNTIME)) { + echoRuntime(command, event); + } else if (action.equals(SUB_VERSION) || action.equals(SUB_VERSION_2)) { + echoVersion(command, event); + } else { + echo404(event); } - MornyCoeur.extra().exec(new SendSticker(event.message().chat().id(), TelegramStickers.ID_404).replyToMessageId(event.message().messageId())); - + } + + /** + * /info 子命令 {@value #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,特殊值跟随上游逻辑 + * @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 ("".equals(id)) TelegramStickers.echoAllStickers(MornyCoeur.extra(), chatId, messageId); + else TelegramStickers.echoStickerByID(id, MornyCoeur.extra(), chatId, messageId); + } + + /** + * /info 子命令 {@value #SUB_RUNTIME} + * @since 1.0.0-alpha4 + */ + public static void echoRuntime (@Nonnull InputCommand command, @Nonnull Update event) { + if (command.getArgs().length == 1) + MornyCommands.onCommandRuntimeExec(event); + else echo404(event); + } + + /** + * /info 子命令 {@value #SUB_VERSION} + * @since 1.0.0-alpha4 + */ + public static void echoVersion (@Nonnull InputCommand command, @Nonnull Update event) { + if (command.getArgs().length == 1) + MornyCommands.onCommandVersionExec(event); + else echo404(event); + } + + private static void echo404 (@Nonnull Update event) { + MornyCoeur.extra().exec(new SendSticker( + event.message().chat().id(), + TelegramStickers.ID_404 + ).replyToMessageId(event.message().messageId())); } } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java index d02beae..0b362f9 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCallMsgSend.java @@ -37,7 +37,7 @@ public class OnCallMsgSend extends EventListener { ) { } @Override - public boolean onMessage(Update update) { + public boolean onMessage(@Nonnull Update update) { // 执行体检查 if (update.message().chat().type() != Chat.Type.Private) return false; diff --git a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java b/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java index fb3f01c..d23d1fe 100644 --- a/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java +++ b/src/main/java/cc/sukazyo/cono/morny/data/TelegramStickers.java @@ -5,6 +5,7 @@ import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendSticker; import com.pengrad.telegrambot.response.SendResponse; +import javax.annotation.Nonnull; import java.lang.reflect.Field; /** @@ -24,7 +25,14 @@ public class TelegramStickers { public static final String ID_PROGYNOVA = "CAACAgUAAxkBAAICm2KEuL7UQqNP7vSPCg2DHJIND6UsAAKLAwACH4WSBszIo722aQ3jJAQ"; public static final String ID_NETWORK_ERR = "CAACAgEAAxkBAAID0WNJgNEkD726KW4vZeFlw0FlVVyNAAIXJgACePzGBb50o7O1RbxoKgQ"; - public static void echoAllStickers (ExtraAction actionObject, long sentChat, int replyToMessageId) { + /** + * 向 telegram 输出当前的 {@link TelegramStickers} 中的所有 stickers. + * @param actionObject 要使用的 telegram account 包装实例 + * @param sentChat 目标 telegram chat id + * @param replyToMessageId 输出时回复指定的消息的 id。使用 {@link -1} 表示不回复消息 + * @since 0.8.0.6 + */ + public static void echoAllStickers (@Nonnull ExtraAction actionObject, long sentChat, int replyToMessageId) { for (Field object : TelegramStickers.class.getFields()) { if (object.getType()==String.class && object.getName().startsWith("ID_")) { @@ -45,7 +53,18 @@ public class TelegramStickers { } - public static void echoStickerByID (String stickerFieldID, ExtraAction actionObject, long sentChat, int replyToMessageId) { + /** + * 向 telegram 输出当前的 {@link TelegramStickers} 中的某个特定 sticker. + * @param stickerFieldID 要输出的 sticker 在 {@link TelegramStickers} 当中的字段名 + * @param actionObject 要使用的 telegram account 包装实例 + * @param sentChat 目标 telegram chat id + * @param replyToMessageId 输出时回复指定的消息的 id。使用 {@link -1} 表示不回复消息 + * @since 0.8.0.6 + */ + public static void echoStickerByID ( + @Nonnull String stickerFieldID, + @Nonnull ExtraAction actionObject, long sentChat, int replyToMessageId + ) { try { // normally get the sticker and echo Field sticker = TelegramStickers.class.getField(stickerFieldID); diff --git a/src/main/java/cc/sukazyo/cono/morny/util/BuildConfigField.java b/src/main/java/cc/sukazyo/cono/morny/util/BuildConfigField.java new file mode 100644 index 0000000..c584a8b --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/util/BuildConfigField.java @@ -0,0 +1,14 @@ +package cc.sukazyo.cono.morny.util; + + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; + +/** + * 这个注解表示当前字段是由 gradle 任务 {@code generateBuildConfig} 自动生成的. + * @since 1.0.0-alpha4 + */ +@Documented +@Target({ElementType.FIELD, ElementType.METHOD}) +public @interface BuildConfigField {}