diff --git a/gradle.properties b/gradle.properties index 40dc41e..4e325fa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,6 +1,6 @@ ## Core -VERSION = 0.8.0.3 +VERSION = 0.8.0.4 CODENAME = fuzhou diff --git a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java index 3f7956d..80572d4 100644 --- a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java +++ b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java @@ -4,7 +4,7 @@ package cc.sukazyo.cono.morny; * the final field that will be updated by gradle automatically. */ public class GradleProjectConfigures { - public static final String VERSION = "0.8.0.3"; + public static final String VERSION = "0.8.0.4"; public static final String CODENAME = "fuzhou"; - public static final long COMPILE_TIMESTAMP = 1664975839021L; + public static final long COMPILE_TIMESTAMP = 1665305196411L; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/command/EncUtils.java b/src/main/java/cc/sukazyo/cono/morny/bot/command/EncUtils.java new file mode 100644 index 0000000..dabdbb8 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/command/EncUtils.java @@ -0,0 +1,116 @@ +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.CommonConvert; +import cc.sukazyo.cono.morny.util.CommonEncrypt; +import cc.sukazyo.cono.morny.util.tgapi.InputCommand; +import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape; +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; +import java.util.Base64; + +public class EncUtils implements ITelegramCommand { + + @Nonnull @Override public String getName () { return "encrypt"; } + @Nullable @Override public String[] getAliases () { return new String[0]; } + @Nonnull @Override public String getParamRule () { return "[algorithm|(l)] [(uppercase)]"; } + @Nonnull @Override public String getDescription () { return "通过指定算法加密回复的内容 (目前只支持文本)"; } + + @Override + public void execute (@Nonnull InputCommand command, @Nonnull Update event) { + + // show a simple help page + // the first paragraph lists available encrypt algorithms, and its aliases. + // with the separator "---", + // the second paragraphs shows the mods available and its aliases. + if (!command.hasArgs() || (command.getArgs()[0].equals("l") && command.getArgs().length==1)) { + MornyCoeur.extra().exec(new SendMessage( + event.message().chat().id(), """ + base64, b64 + base64decode, base64d, b64d + sha1 + sha256 + sha512 + md5 + --- + uppercase, upper, u (sha1/sha256/sha512/md5 only) + """ + ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); + return; + } + + // param1 is the encrypting algorithm, it MUST EXIST. + // so the mod will be set in param2. + // and for now only support UPPERCASE mod, so it exists in param2, or there should no any params. + boolean modUpperCase = false; + if (command.getArgs().length > 1) { + if (command.getArgs().length < 3 && ( + command.getArgs()[1].equalsIgnoreCase("uppercase") || + command.getArgs()[1].equalsIgnoreCase("u") || + command.getArgs()[1].equalsIgnoreCase("upper") + )) { + modUpperCase = true; + } else { + MornyCoeur.extra().exec(new SendSticker( + event.message().chat().id(), TelegramStickers.ID_404 + ).replyToMessageId(event.message().messageId())); + return; + } + } + + // for now, only support reply to A TEXT MESSAGE and encrypt/hash the text value. + // if there's no text message in reply, it will report null as result. + if (event.message().replyToMessage() == null || event.message().replyToMessage().text() == null) { + MornyCoeur.extra().exec(new SendMessage( + event.message().chat().id(), + "null" + ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); + return; + } + final String data = event.message().replyToMessage().text(); + + String result; + switch (command.getArgs()[0]) { + case "base64", "b64" -> result = Base64.getEncoder().encodeToString(data.getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET)); + case "base64decode", "base64d", "b64d" -> result = new String( + Base64.getDecoder().decode(data.getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET)), CommonEncrypt.ENCRYPT_STANDARD_CHARSET); + case "md5" -> result = CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(data)); + case "sha1" -> result = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha1(data)); + case "sha256" -> result = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha256(data)); + case "sha512" -> result = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha512(data)); + default -> { + MornyCoeur.extra().exec(new SendSticker( + event.message().chat().id(), TelegramStickers.ID_404 + ).replyToMessageId(event.message().messageId())); + return; + } + } + if (modUpperCase) { + // modUpperCase support only algorithm that showed as HEX value. + // it means md5, sha1, sha256, sha512 here. + // other will report wrong param. + switch (command.getArgs()[0]) { + case "md5", "sha1", "sha256", "sha512" -> + result = result.toUpperCase(); + default -> { + MornyCoeur.extra().exec(new SendSticker( + event.message().chat().id(), TelegramStickers.ID_404 + ).replyToMessageId(event.message().messageId())); + return; + } + } + } + MornyCoeur.extra().exec(new SendMessage( + event.message().chat().id(), + "
" + MsgEscape.escapeHtml(result) + "
" + ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); + + } + +} 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 6c7191c..85c968a 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 @@ -68,6 +68,7 @@ public class MornyCommands { new Nbnhhsh(), new Ip186Query.Ip(), new Ip186Query.Whois(), + new EncUtils(), new SaveData(), new Version(), new MornyRuntime(), diff --git a/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java b/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java index f0dcedd..256a217 100644 --- a/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java +++ b/src/main/java/cc/sukazyo/cono/morny/util/CommonEncrypt.java @@ -5,9 +5,12 @@ import java.nio.charset.Charset; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Base64; /** * 用于数据加密或编解码的工具类. + *

+ * 出于 java std 中 Base64 的 {@link Base64.Encoder encode}/{@link Base64.Decoder decode} 十分好用,在此不再进行包装。 */ public class CommonEncrypt { @@ -18,6 +21,15 @@ public class CommonEncrypt { */ public static final Charset ENCRYPT_STANDARD_CHARSET = StandardCharsets.UTF_8; + @Nonnull + private static byte[] hashAsJavaMessageDigest(String algorithm, @Nonnull byte[] data) { + try { + return MessageDigest.getInstance(algorithm).digest(data); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + } + /** * 取得数据的 md5 散列值. * @@ -26,12 +38,7 @@ public class CommonEncrypt { */ @Nonnull public static byte[] hashMd5 (@Nonnull byte[] data) { - try { - return MessageDigest.getInstance("md5").digest(data); - } catch (NoSuchAlgorithmException e) { - throw new IllegalStateException(); - } - + return hashAsJavaMessageDigest("md5", data); } /** @@ -47,4 +54,76 @@ public class CommonEncrypt { return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET)); } + /** + * 取得数据的 sha1 散列值. + * + * @param data byte 数组形式的数据体 + * @return 二进制(byte数组)格式的数据的 sha1 散列值 + */ + @Nonnull + public static byte[] hashSha1 (@Nonnull byte[] data) { + return hashAsJavaMessageDigest("sha1", data); + } + + /** + * 取得一个字符串的 sha1 散列值. + *

+ * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析 + * + * @param originString 要进行散列的字符串 + * @return 二进制(byte数组)格式的 sha1 散列值 + */ + @Nonnull + public static byte[] hashSha1 (String originString) { + return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET)); + } + + /** + * 取得数据的 sha256 散列值. + * + * @param data byte 数组形式的数据体 + * @return 二进制(byte数组)格式的数据的 sha256 散列值 + */ + @Nonnull + public static byte[] hashSha256 (@Nonnull byte[] data) { + return hashAsJavaMessageDigest("sha256", data); + } + + /** + * 取得一个字符串的 sha256 散列值. + *

+ * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析 + * + * @param originString 要进行散列的字符串 + * @return 二进制(byte数组)格式的 sha256 散列值 + */ + @Nonnull + public static byte[] hashSha256 (String originString) { + return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET)); + } + + /** + * 取得数据的 sha512 散列值. + * + * @param data byte 数组形式的数据体 + * @return 二进制(byte数组)格式的数据的 sha512 散列值 + */ + @Nonnull + public static byte[] hashSha512 (@Nonnull byte[] data) { + return hashAsJavaMessageDigest("md5", data); + } + + /** + * 取得一个字符串的 sha512 散列值. + *

+ * 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析 + * + * @param originString 要进行散列的字符串 + * @return 二进制(byte数组)格式的 sha512 散列值 + */ + @Nonnull + public static byte[] hashSha512 (String originString) { + return hashMd5(originString.getBytes(ENCRYPT_STANDARD_CHARSET)); + } + }