[[[release 0.8.0.11*putian]]]

## 📇功能

- 关闭了 tracker 功能(以及现在 morny 确实的没有任何文件 IO 了)
- 添加 /encrypt 命令可以对文本使用加密算法处理
  - 支持文本,图片,文件,但不支援复数个图片/文件,同时支援的大小受到 API 限制
  - 支援 base64(enc/dec,url-format) md5, sha1/256/512
  - md5 以及 sha1/256/512 支援 uppercase 选项
- 添加内联查询 twitter 分享链接 format 为 vxtwitter 链接的功能
- 添加内联查询 bilibili 分享链接或av/bv号 format 为 av/bv 视频链接的功能
  - 目前仅支援 bilibili av/bv 视频链接不支援 b23 分享链接
- 添加 /info 命令
  - 目前仅支援 stickers (以及stickers.ID) 选项输出 morny 使用的贴纸
- 添加了可以输出一个之前加进去但是没用到的贴纸的 /install 命令

## 🔌系统接口

- 将所有之前在 sukazyo.cc:untitled-* 的工具类重新放回了 morny.utils 下
  - CommonCommand 名称改为了 UniversalCommand
  - 也将之前的 TelegramUserInformation 和 TGToStringMessage 移动到了 utils.tgapi 的相应位置
  - stringsConnect 添加进了 CommonConvert 当中
- 删除了 EncryptUtils,与之代替的是 CommonEncrypt
  - 之前放在 EncryptUtils 的 byte[] 转 hex string 的工具转移到了新的 CommonConvert 当中
  - encryptByXXX 的命名改为了 hashXXX 的命名(虽然由于目前都是散列工具)
  - CommonEncrypt 也添加了可以用于规范化的 ENC_STD_CHARSET 字段,以及 sha1/256/512 一系列工具方法
- CommonFormatUtils 也跟随上文,类名改为了 CommonFormat(内容没有变)
- 为这三个 utils.CommonXXX 都添加了 Test 类

## 🔩技术修改/typo

- CommonFormat.formatDate 中定义日期格式的字符串独立为了一个字段
- CommonConvert byte -> hex-char 的实现方式改为了 Integer.toHexString
This commit is contained in:
A.C.Sukazyo Eyre 2022-11-02 16:33:44 +08:00
commit 3661cb1264
Signed by: Eyre_S
GPG Key ID: C17CE40291207874
60 changed files with 1392 additions and 161 deletions

3
.gitignore vendored
View File

@ -4,7 +4,8 @@
.vscode/ .vscode/
.gradle/ .gradle/
.settings/ .settings/
/src/test/* /src/test/java/test/*
/src/test/resources/test/*
#build #build
/build/ /build/

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "_book"]
path = _book
url = https://storage.sukazyo.cc/Eyre_S/morny-book.git

View File

@ -2,7 +2,7 @@
[tg-account]: https://t.me/morny_cono_annie_bot [tg-account]: https://t.me/morny_cono_annie_bot
[issues]: https://github.com/Eyre-S/Coeur-Morny-Cono/issues [issues]: https://github.com/Eyre-S/Coeur-Morny-Cono/issues
[todo]: https://github.com/users/Eyre-S/projects/1 [todo]: https://github.com/users/Eyre-S/projects/1
[artifact]: https://mvn.sukazyo.cc/main/cc/sukazyo/morny-coeur [artifact]: https://mvn.sukazyo.cc/#/releases/cc/sukazyo/morny-coeur
[tg4j]: https://github.com/pengrad/java-telegram-bot-api [tg4j]: https://github.com/pengrad/java-telegram-bot-api
[spotbugs]: https://spotbugs.github.io/ [spotbugs]: https://spotbugs.github.io/

1
_book Submodule

@ -0,0 +1 @@
Subproject commit 3072bcee8e498e87ecdd36958185ad423e80bcf3

View File

@ -17,24 +17,16 @@ repositories {
maven { name '-ws'; url 'https://mvn.sukazyo.cc/releases' } maven { name '-ws'; url 'https://mvn.sukazyo.cc/releases' }
} }
String untitled (String lib, String upd = null) {
int majorCode = Integer.parseInt(project.libUntitledVersionMajor)
return "cc.sukazyo.untitled:$lib:[$majorCode${upd==null?"":".$upd"}, ${majorCode+1}["
}
dependencies { dependencies {
compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${libSpotbugsVersion}" compileOnlyApi "com.github.spotbugs:spotbugs-annotations:${libSpotbugsVersion}"
implementation untitled("util-command-parser","1.0")
implementation untitled("util-string-commons", "1.0")
implementation untitled("util-telegram-api", "2.1")
implementation untitled("util-telegram-api-formatter", "3.3")
implementation untitled("util-telegram-commons", "1.0")
api "cc.sukazyo:messiva:${libMessivaVersion}" api "cc.sukazyo:messiva:${libMessivaVersion}"
implementation "com.github.pengrad:java-telegram-bot-api:${libJavaTelegramBotApiVersion}" implementation "com.github.pengrad:java-telegram-bot-api:${libJavaTelegramBotApiVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-api:${libJunitVersion}" testImplementation "org.junit.jupiter:junit-jupiter-api:${libJunitVersion}"
testImplementation "org.junit.jupiter:junit-jupiter-params:${libJunitVersion}"
testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${libJunitVersion}" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:${libJunitVersion}"
} }

View File

@ -1,15 +1,13 @@
## Core ## Core
VERSION = 0.7.2.1 VERSION = 0.8.0.11
CODENAME = fuzhou CODENAME = putian
# dependencies # dependencies
libSpotbugsVersion = 4.7.2 libSpotbugsVersion = 4.7.2
libUntitledVersionMajor = 1
libMessivaVersion = 0.1.0.1 libMessivaVersion = 0.1.0.1
libJavaTelegramBotApiVersion = 5.6.0 libJavaTelegramBotApiVersion = 5.6.0

View File

@ -4,7 +4,7 @@ package cc.sukazyo.cono.morny;
* the final field that will be updated by gradle automatically. * the final field that will be updated by gradle automatically.
*/ */
public class GradleProjectConfigures { public class GradleProjectConfigures {
public static final String VERSION = "0.7.2.1"; public static final String VERSION = "0.8.0.11";
public static final String CODENAME = "fuzhou"; public static final String CODENAME = "putian";
public static final long COMPILE_TIMESTAMP = 1664590869773L; public static final long COMPILE_TIMESTAMP = 1667376095614L;
} }

View File

@ -6,7 +6,7 @@ import cc.sukazyo.cono.morny.bot.event.EventListeners;
import cc.sukazyo.cono.morny.bot.query.MornyQueries; import cc.sukazyo.cono.morny.bot.query.MornyQueries;
import cc.sukazyo.cono.morny.daemon.MornyDaemons; import cc.sukazyo.cono.morny.daemon.MornyDaemons;
import cc.sukazyo.cono.morny.daemon.TrackerDataManager; import cc.sukazyo.cono.morny.daemon.TrackerDataManager;
import cc.sukazyo.untitled.telegram.api.extra.ExtraAction; import cc.sukazyo.cono.morny.util.tgapi.ExtraAction;
import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.impl.FileApi; import com.pengrad.telegrambot.impl.FileApi;
import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.User;
@ -59,7 +59,7 @@ public class MornyCoeur {
* {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock} * {@link cc.sukazyo.cono.morny.bot.event.OnUpdateTimestampOffsetLock}
* 会根据这里定义的时间戳取消掉比此时间更早的事件链 * 会根据这里定义的时间戳取消掉比此时间更早的事件链
*/ */
public long latestEventTimestamp; public final long latestEventTimestamp;
/** /**
* morny 主程序启动时间<br> * morny 主程序启动时间<br>
* 用于统计数据 * 用于统计数据

View File

@ -1,6 +1,6 @@
package cc.sukazyo.cono.morny; package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.util.CommonFormatUtils; import cc.sukazyo.cono.morny.util.CommonFormat;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -205,7 +205,7 @@ public class ServerMain {
MornySystem.VERSION, MornySystem.CODENAME.toUpperCase(), MornySystem.VERSION, MornySystem.CODENAME.toUpperCase(),
MornySystem.getJarMd5(), MornySystem.getJarMd5(),
GradleProjectConfigures.COMPILE_TIMESTAMP, GradleProjectConfigures.COMPILE_TIMESTAMP,
CommonFormatUtils.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0) CommonFormat.formatDate(GradleProjectConfigures.COMPILE_TIMESTAMP, 0)
)); ));
return; return;

View File

@ -1,10 +1,9 @@
package cc.sukazyo.cono.morny.bot.api; package cc.sukazyo.cono.morny.bot.api;
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import cc.sukazyo.untitled.telegram.api.event.EventRuntimeException;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;

View File

@ -1,7 +1,7 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.DeleteMessage; import com.pengrad.telegrambot.request.DeleteMessage;

View File

@ -0,0 +1,205 @@
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.PhotoSize;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.GetFile;
import com.pengrad.telegrambot.request.SendDocument;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Base64;
import static cc.sukazyo.cono.morny.Log.logger;
public class Encryptor 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(), """
<b><u>base64</u></b>, b64
<b><u>base64url</u></b>, base64u, b64u
<b><u>base64decode</u></b>, base64d, b64d
<b><u>base64url-decode</u></b>, base64ud, b64ud
<b><u>sha1</u></b>
<b><u>sha256</u></b>
<b><u>sha512</u></b>
<b><u>md5</u></b>
---
<b><i>uppercase</i></b>, upper, u <i>(sha1/sha256/sha512/md5 only)</i>
"""
).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 or ONE UNIVERSAL FILE
// if the replied message contains a UNIVERSAL FILE, it will use the file and will not use the text with it
// do not support TELEGRAM INLINE IMAGE/VIDEO/AUDIO yet
// do not support MULTI_FILE yet
// if there's no text message in reply, it will report null as result.
boolean inputText;
byte[] data;
String dataName;
if (event.message().replyToMessage() != null && event.message().replyToMessage().document() != null) {
inputText = false;
try {
data = MornyCoeur.getAccount().getFileContent(MornyCoeur.extra().exec(new GetFile(
event.message().replyToMessage().document().fileId()
)).file());
} catch (IOException e) {
logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage());
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_NETWORK_ERR
).replyToMessageId(event.message().messageId()));
return;
}
dataName = event.message().replyToMessage().document().fileName();
} else if (event.message().replyToMessage() != null && event.message().replyToMessage().photo() != null) {
inputText = false;
try {
PhotoSize originPhoto = null;
long photoSize = 0;
for (PhotoSize size : event.message().replyToMessage().photo()) if (photoSize < (long)size.width() *size.height()) {
originPhoto = size;
photoSize = (long)size.width() *size.height();
} // found max size (original) image in available sizes
if (originPhoto==null) throw new IOException("no photo object from api.");
data = MornyCoeur.getAccount().getFileContent(MornyCoeur.extra().exec(new GetFile(
originPhoto.fileId()
)).file());
} catch (IOException e) {
logger.warn("NetworkRequest error: TelegramFileAPI:\n\t" + e.getMessage());
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(),
TelegramStickers.ID_NETWORK_ERR
).replyToMessageId(event.message().messageId()));
return;
}
dataName = "photo"+CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(String.valueOf(System.currentTimeMillis()))).substring(32-12).toUpperCase()+".png";
} else if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null) {
inputText = true;
data = event.message().replyToMessage().text().getBytes(CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
dataName = null;
} else {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<i><u>null</u></i>"
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
return;
}
boolean echoString = true;
String resultString = null;
byte[] result = null;
String resultName = null;
switch (command.getArgs()[0]) {
case "base64", "b64", "base64url", "base64u", "b64u" -> {
final Base64.Encoder b64tool = command.getArgs()[0].contains("u") ? Base64.getUrlEncoder() : Base64.getEncoder();
result = b64tool.encode(data);
if (!inputText) {
echoString = false;
resultName = dataName+".b64.txt";
} else {
resultString = new String(result, CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
}
}
case "base64decode", "base64d", "b64d", "base64url-decode", "base64ud", "b64ud" -> {
final Base64.Decoder b64tool = command.getArgs()[0].contains("u") ? Base64.getUrlDecoder() : Base64.getDecoder();
try { result = b64tool.decode(data); }
catch (IllegalArgumentException e) {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
if (!inputText) {
echoString = false;
resultName = CommonEncrypt.base64FilenameLint(dataName);
} else {
resultString = new String(result, CommonEncrypt.ENCRYPT_STANDARD_CHARSET);
}
}
case "md5" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(data));
case "sha1" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha1(data));
case "sha256" -> resultString = CommonConvert.byteArrayToHex(CommonEncrypt.hashSha256(data));
case "sha512" -> resultString = 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" -> {
assert resultString != null;
resultString = resultString.toUpperCase();
}
default -> {
MornyCoeur.extra().exec(new SendSticker(
event.message().chat().id(), TelegramStickers.ID_404
).replyToMessageId(event.message().messageId()));
return;
}
}
}
if (echoString) {
MornyCoeur.extra().exec(new SendMessage(
event.message().chat().id(),
"<pre><code>" + MsgEscape.escapeHtml(resultString) + "</code></pre>"
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
} else {
MornyCoeur.extra().exec(new SendDocument(
event.message().chat().id(),
result
).fileName(resultName).replyToMessageId(event.message().messageId()));
}
}
}

View File

@ -4,8 +4,8 @@ import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornyTrusted; import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle; import cc.sukazyo.cono.morny.bot.event.OnEventHackHandle;
import cc.sukazyo.cono.morny.data.TelegramStickers; import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.untitled.util.telegram.object.InputCommand;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendSticker; import com.pengrad.telegrambot.request.SendSticker;

View File

@ -1,8 +1,8 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.TelegramUserInformation; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.User;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;

View File

@ -1,6 +1,6 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;

View File

@ -2,8 +2,8 @@ package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse; import cc.sukazyo.cono.morny.data.ip186.IP186QueryResponse;
import cc.sukazyo.untitled.util.telegram.object.InputCommand;
import cc.sukazyo.cono.morny.data.ip186.IP186QueryHandler; 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.Update;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
@ -12,7 +12,8 @@ import org.jetbrains.annotations.NotNull;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
/** /**
* {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端 * {@value IP186QueryHandler#SITE_URL} 查询的 telegram 命令前端

View File

@ -5,8 +5,8 @@ import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornySystem; import cc.sukazyo.cono.morny.MornySystem;
import cc.sukazyo.cono.morny.data.MornyJrrp; import cc.sukazyo.cono.morny.data.MornyJrrp;
import cc.sukazyo.cono.morny.data.TelegramStickers; import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.untitled.telegram.api.formatting.TGToString; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.pengrad.telegrambot.model.BotCommand; import com.pengrad.telegrambot.model.BotCommand;
import com.pengrad.telegrambot.model.DeleteMyCommands; import com.pengrad.telegrambot.model.DeleteMyCommands;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
@ -27,9 +27,9 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import static cc.sukazyo.cono.morny.Log.logger; import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.CommonFormatUtils.formatDate; import static cc.sukazyo.cono.morny.util.CommonFormat.formatDate;
import static cc.sukazyo.cono.morny.util.CommonFormatUtils.formatDuration; import static cc.sukazyo.cono.morny.util.CommonFormat.formatDuration;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class MornyCommands { public class MornyCommands {
@ -57,6 +57,7 @@ public class MornyCommands {
} }
} }
@SuppressWarnings("NonAsciiCharacters")
public MornyCommands () { public MornyCommands () {
register( register(
@ -67,7 +68,9 @@ public class MornyCommands {
new Nbnhhsh(), new Nbnhhsh(),
new Ip186Query.Ip(), new Ip186Query.Ip(),
new Ip186Query.Whois(), new Ip186Query.Whois(),
new Encryptor(),
new SaveData(), new SaveData(),
new MornyInformations(),
new Version(), new Version(),
new MornyRuntime(), new MornyRuntime(),
new Jrrp(), new Jrrp(),
@ -76,6 +79,7 @@ public class MornyCommands {
// 特殊的命令 // 特殊的命令
register( register(
new Testing(),
new DirectMsgClear() new DirectMsgClear()
); );
@ -85,7 +89,8 @@ public class MornyCommands {
new 喵呜.揉揉(), new 喵呜.揉揉(),
new 喵呜.蹭蹭(), new 喵呜.蹭蹭(),
new 喵呜.贴贴(), new 喵呜.贴贴(),
new 私わね() new 私わね(),
new 喵呜.Progynova()
); );
} }

View File

@ -0,0 +1,46 @@
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.Update;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class MornyInformations implements ITelegramCommand {
private static final String ACT_STICKER = "stickers";
@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 当前版本的一些预定义信息"; }
@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()));
}
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;
}
MornyCoeur.extra().exec(new SendSticker(event.message().chat().id(), TelegramStickers.ID_404).replyToMessageId(event.message().messageId()));
}
}

View File

@ -1,18 +1,18 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.data.NbnhhshQuery; import cc.sukazyo.cono.morny.data.NbnhhshQuery;
import cc.sukazyo.untitled.util.string.StringArrays;
import cc.sukazyo.untitled.util.telegram.object.InputCommand;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; 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 { public class Nbnhhsh implements ITelegramCommand {
@ -30,7 +30,7 @@ public class Nbnhhsh implements ITelegramCommand {
if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null) if (event.message().replyToMessage() != null && event.message().replyToMessage().text() != null)
queryTarget = event.message().replyToMessage().text(); queryTarget = event.message().replyToMessage().text();
if (command.hasArgs()) if (command.hasArgs())
queryTarget = StringArrays.connectStringArray(command.getArgs(), " ", 0, command.getArgs().length-1); queryTarget = stringsConnecting(command.getArgs(), " ", 0, command.getArgs().length-1);
NbnhhshQuery.GuessResult response = NbnhhshQuery.sendGuess(queryTarget); NbnhhshQuery.GuessResult response = NbnhhshQuery.sendGuess(queryTarget);

View File

@ -0,0 +1,36 @@
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(),
"<b>Just<b/> a TEST command."
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
}
}

View File

@ -1,10 +1,12 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; 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.Update;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -56,4 +58,17 @@ public class 喵呜 {
} }
} }
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()));
}
}
} }

View File

@ -1,7 +1,7 @@
package cc.sukazyo.cono.morny.bot.command; package cc.sukazyo.cono.morny.bot.command;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;

View File

@ -5,7 +5,7 @@ import cc.sukazyo.cono.morny.bot.api.EventListenerManager;
public class EventListeners { public class EventListeners {
public static final OnTelegramCommand COMMANDS_LISTENER = new OnTelegramCommand(); public static final OnTelegramCommand COMMANDS_LISTENER = new OnTelegramCommand();
public static final OnActivityRecord ACTIVITY_RECORDER = new OnActivityRecord(); @SuppressWarnings("unused") public static final OnActivityRecord ACTIVITY_RECORDER = new OnActivityRecord();
public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction(); public static final OnUserSlashAction USER_SLASH_ACTION = new OnUserSlashAction();
public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock(); public static final OnUpdateTimestampOffsetLock UPDATE_TIMESTAMP_OFFSET_LOCK = new OnUpdateTimestampOffsetLock();
public static final OnInlineQueries INLINE_QUERY = new OnInlineQueries(); public static final OnInlineQueries INLINE_QUERY = new OnInlineQueries();
@ -19,7 +19,7 @@ public class EventListeners {
public static void registerAllListeners () { public static void registerAllListeners () {
EventListenerManager.addListener( EventListenerManager.addListener(
ACTIVITY_RECORDER, // ACTIVITY_RECORDER,
UPDATE_TIMESTAMP_OFFSET_LOCK, UPDATE_TIMESTAMP_OFFSET_LOCK,
/* write functional event behind here */ /* write functional event behind here */
// KUOHUANHUAN_NEED_SLEEP, // KUOHUANHUAN_NEED_SLEEP,

View File

@ -4,9 +4,9 @@ import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornyTrusted; import cc.sukazyo.cono.morny.MornyTrusted;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.data.TelegramStickers; import cc.sukazyo.cono.morny.data.TelegramStickers;
import cc.sukazyo.cono.morny.util.CommonFormatUtils; import cc.sukazyo.cono.morny.util.CommonFormat;
import cc.sukazyo.untitled.telegram.api.formatting.TGToString; import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape;
import cc.sukazyo.untitled.util.telegram.formatting.MsgEscape; import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
@ -122,9 +122,9 @@ public class OnCallMe extends EventListener {
event.message().from().id(), event.message().from().id(),
String.format("<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>", String.format("<i>on</i> <code>%s [UTC+8]</code>\n- <code>%s</code> <i>before</i>",
MsgEscape.escapeHtml( MsgEscape.escapeHtml(
CommonFormatUtils.formatDate((long)lastDinnerData.forwardDate()*1000, 8) CommonFormat.formatDate((long)lastDinnerData.forwardDate()*1000, 8)
), MsgEscape.escapeHtml( ), MsgEscape.escapeHtml(
CommonFormatUtils.formatDuration(System.currentTimeMillis()-(long)lastDinnerData.forwardDate()*1000) CommonFormat.formatDuration(System.currentTimeMillis()-(long)lastDinnerData.forwardDate()*1000)
) )
) )
).replyToMessageId(sendResp.message().messageId()).parseMode(ParseMode.HTML)); ).replyToMessageId(sendResp.message().messageId()).parseMode(ParseMode.HTML));

View File

@ -22,7 +22,8 @@ import cc.sukazyo.cono.morny.data.TelegramStickers;
import com.pengrad.telegrambot.response.GetChatResponse; import com.pengrad.telegrambot.response.GetChatResponse;
import com.pengrad.telegrambot.response.SendResponse; import com.pengrad.telegrambot.response.SendResponse;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnCallMsgSend extends EventListener { public class OnCallMsgSend extends EventListener {

View File

@ -2,7 +2,7 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.untitled.util.telegram.formatting.MsgEscape; import cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape;
import com.google.gson.GsonBuilder; import com.google.gson.GsonBuilder;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;

View File

@ -2,7 +2,7 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.untitled.util.telegram.object.InputCommand; import cc.sukazyo.cono.morny.util.tgapi.InputCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;

View File

@ -2,7 +2,7 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.untitled.util.command.CommonCommand; import cc.sukazyo.cono.morny.util.UniversalCommand;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@ -22,7 +22,7 @@ public class OnUserRandoms extends EventListener {
if (update.message().text() == null) return false; if (update.message().text() == null) return false;
if (!update.message().text().startsWith("/")) return false; if (!update.message().text().startsWith("/")) return false;
final String[] preProcess = CommonCommand.format(update.message().text()); final String[] preProcess = UniversalCommand.format(update.message().text());
if (preProcess.length > 1) return false; if (preProcess.length > 1) return false;
final String query = preProcess[0]; final String query = preProcess[0];

View File

@ -2,9 +2,8 @@ package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.bot.api.EventListener; import cc.sukazyo.cono.morny.bot.api.EventListener;
import cc.sukazyo.cono.morny.util.tgapi.TGToStringFromMessage; import cc.sukazyo.cono.morny.util.UniversalCommand;
import cc.sukazyo.untitled.util.command.CommonCommand; import cc.sukazyo.cono.morny.util.tgapi.formatting.TGToString;
import cc.sukazyo.untitled.util.string.StringArrays;
import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
@ -13,7 +12,8 @@ import com.pengrad.telegrambot.request.SendMessage;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; import static cc.sukazyo.cono.morny.util.CommonConvert.stringsConnecting;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class OnUserSlashAction extends EventListener { public class OnUserSlashAction extends EventListener {
@ -38,7 +38,7 @@ public class OnUserSlashAction extends EventListener {
// return false; // return false;
// } // }
final String[] action = CommonCommand.format(text); final String[] action = UniversalCommand.format(text);
action[0] = action[0].substring(1); action[0] = action[0].substring(1);
if (action[0].matches("^\\w+(@\\w+)?$")) { if (action[0].matches("^\\w+(@\\w+)?$")) {
@ -53,7 +53,7 @@ public class OnUserSlashAction extends EventListener {
final boolean hasObject = action.length != (isHardParse?2:1); final boolean hasObject = action.length != (isHardParse?2:1);
final String object = final String object =
hasObject ? hasObject ?
StringArrays.connectStringArray(action, " ", isHardParse?2:1, action.length-1) : stringsConnecting(action, " ", isHardParse?2:1, action.length-1) :
""; "";
final Message origin = event.message(); final Message origin = event.message();
final Message target = (event.message().replyToMessage() == null ? ( final Message target = (event.message().replyToMessage() == null ? (
@ -66,11 +66,11 @@ public class OnUserSlashAction extends EventListener {
event.message().chat().id(), event.message().chat().id(),
String.format( String.format(
"%s %s%s %s %s!", "%s %s%s %s %s!",
TGToStringFromMessage.as(origin).getSenderFirstNameRefHtml(), TGToString.as(origin).getSenderFirstNameRefHtml(),
escapeHtml(verb), escapeHtml((hasObject?"":"")), escapeHtml(verb), escapeHtml((hasObject?"":"")),
origin==target ? origin==target ?
"<a href='tg://user?id="+TGToStringFromMessage.as(target).getSenderId()+"'>自己</a>" : "<a href='tg://user?id="+TGToString.as(target).getSenderId()+"'>自己</a>" :
TGToStringFromMessage.as(target).getSenderFirstNameRefHtml(), TGToString.as(target).getSenderFirstNameRefHtml(),
escapeHtml(hasObject ? object+" " : "") escapeHtml(hasObject ? object+" " : "")
) )
).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId())); ).parseMode(ParseMode.HTML).replyToMessageId(event.message().messageId()));

View File

@ -4,11 +4,12 @@ import javax.annotation.Nullable;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit; import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResult;
public interface ITelegramQuery <T extends InlineQueryResult<T>> { import java.util.List;
public interface ITelegramQuery {
@Nullable @Nullable
InlineQueryUnit<T> query (Update event); List<InlineQueryUnit<?>> query (Update event);
} }

View File

@ -9,19 +9,21 @@ import java.util.List;
public class MornyQueries { public class MornyQueries {
private final List<ITelegramQuery<?>> queryInstances = new ArrayList<>(); private final List<ITelegramQuery> queryInstances = new ArrayList<>();
public MornyQueries () { public MornyQueries () {
queryInstances.add(new RawText()); queryInstances.add(new RawText());
queryInstances.add(new MyInformation()); queryInstances.add(new MyInformation());
queryInstances.add(new ShareToolTwitter());
queryInstances.add(new ShareToolBilibili());
} }
@Nonnull @Nonnull
public List<InlineQueryUnit<?>> query (@Nonnull Update event) { public List<InlineQueryUnit<?>> query (@Nonnull Update event) {
final List<InlineQueryUnit<?>> results = new ArrayList<>(); final List<InlineQueryUnit<?>> results = new ArrayList<>();
for (ITelegramQuery<?> instance : queryInstances) { for (ITelegramQuery instance : queryInstances) {
final InlineQueryUnit<?> r = instance.query(event); final List<InlineQueryUnit<?>> r = instance.query(event);
if (r!=null) results.add(r); if (r!=null) results.addAll(r);
} }
return results; return results;
} }

View File

@ -8,23 +8,28 @@ import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent; import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
import cc.sukazyo.cono.morny.util.TelegramUserInformation; import cc.sukazyo.cono.morny.util.tgapi.formatting.TelegramUserInformation;
public class MyInformation implements ITelegramQuery<InlineQueryResultArticle> { import java.util.Collections;
import java.util.List;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class MyInformation implements ITelegramQuery {
public static final String ID_PREFIX = "[morny/info/me]"; public static final String ID_PREFIX = "[morny/info/me]";
public static final String TITLE = "My Account Information"; public static final String TITLE = "My Account Information";
@Override @Override
@Nullable @Nullable
public InlineQueryUnit<InlineQueryResultArticle> query(Update event) { public List<InlineQueryUnit<?>> query(Update event) {
if (!(event.inlineQuery().query() == null || "".equals(event.inlineQuery().query()))) return null; if (!(event.inlineQuery().query() == null || "".equals(event.inlineQuery().query()))) return null;
return new InlineQueryUnit<>(new InlineQueryResultArticle( return Collections.singletonList(new InlineQueryUnit<>(new InlineQueryResultArticle(
ID_PREFIX, TITLE, inlineIds(ID_PREFIX), TITLE,
new InputTextMessageContent( new InputTextMessageContent(
TelegramUserInformation.informationOutputHTML(event.inlineQuery().from()) TelegramUserInformation.informationOutputHTML(event.inlineQuery().from())
).parseMode(ParseMode.HTML) ).parseMode(ParseMode.HTML)
)).isPersonal(true).cacheTime(10); )).isPersonal(true).cacheTime(10));
} }
} }

View File

@ -1,7 +1,6 @@
package cc.sukazyo.cono.morny.bot.query; package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit; import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import cc.sukazyo.cono.morny.util.EncryptUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -9,20 +8,24 @@ import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle; import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent; import com.pengrad.telegrambot.model.request.InputTextMessageContent;
public class RawText implements ITelegramQuery<InlineQueryResultArticle> { import java.util.Collections;
import java.util.List;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class RawText implements ITelegramQuery {
public static final String ID_PREFIX = "[morny/r/text]"; public static final String ID_PREFIX = "[morny/r/text]";
public static final String TITLE = "Raw Text"; public static final String TITLE = "Raw Text";
@Override @Override
@Nullable @Nullable
public InlineQueryUnit<InlineQueryResultArticle> query (Update event) { public List<InlineQueryUnit<?>> query (Update event) {
if (event.inlineQuery().query() == null || "".equals(event.inlineQuery().query())) return null; if (event.inlineQuery().query() == null || "".equals(event.inlineQuery().query())) return null;
return new InlineQueryUnit<>(new InlineQueryResultArticle( return Collections.singletonList(new InlineQueryUnit<>(new InlineQueryResultArticle(
ID_PREFIX + EncryptUtils.encryptByMD5(event.inlineQuery().query()), inlineIds(ID_PREFIX, event.inlineQuery().query()), TITLE,
TITLE,
new InputTextMessageContent(event.inlineQuery().query()) new InputTextMessageContent(event.inlineQuery().query())
)); )));
} }
} }

View File

@ -0,0 +1,80 @@
package cc.sukazyo.cono.morny.bot.query;
import cc.sukazyo.cono.morny.bot.api.InlineQueryUnit;
import cc.sukazyo.cono.morny.util.BiliTool;
import com.pengrad.telegrambot.model.Update;
import com.pengrad.telegrambot.model.request.InlineQueryResultArticle;
import com.pengrad.telegrambot.model.request.InputTextMessageContent;
import com.pengrad.telegrambot.model.request.ParseMode;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
//import static cc.sukazyo.cono.morny.Log.logger;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class ShareToolBilibili implements ITelegramQuery {
public static final String TITLE_BILI_AV = "[bilibili] Share video / av";
public static final String TITLE_BILI_BV = "[bilibili] Share video / BV";
public static final String ID_PREFIX_BILI_AV = "[morny/share/bili/av]";
public static final String ID_PREFIX_BILI_BV = "[morny/share/bili/bv]";
public static final Pattern REGEX_BILI_VIDEO = Pattern.compile("^(?:(?:https?://)?(?:www\\.)?bilibili\\.com(?:/s)?/video/((?:av|AV)(\\d+)|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]+))/?(\\?(?:p=(\\d+))?.*)?|(?:av|AV)(\\d+)|(?:bv|BV)([A-HJ-NP-Za-km-z1-9]+))$");
private static final String SHARE_FORMAT_HTML = "<a href='%s'>%s</a>";
@Nullable
@Override
public List<InlineQueryUnit<?>> query (Update event) {
if (event.inlineQuery().query() == null) return null;
final Matcher regex = REGEX_BILI_VIDEO.matcher(event.inlineQuery().query());
if (regex.matches()) {
// logger.debug(String.format(
// "====== ok\n1: %s\n2: %s\n3: %s\n4: %s\n5: %s\n6: %s\n7: %s",
// regex.group(1), regex.group(2), regex.group(3), regex.group(4),
// regex.group(5), regex.group(6), regex.group(7)
// ));
// get video id from input, also get video part id
String av = regex.group(2)==null ? regex.group(6)==null ? null : regex.group(6) : regex.group(2);
String bv = regex.group(3)==null ? regex.group(7)==null ? null : regex.group(7) : regex.group(3);
// logger.trace(String.format("catch id av[%s] bv[%s]", av, bv));
final int part = regex.group(5)==null ? -1 : Integer.parseInt(regex.group(5));
// logger.trace(String.format("catch part [%s]", part));
if (av == null) {
assert bv != null;
av = String.valueOf(BiliTool.toAv(bv));
// logger.trace(String.format("converted bv[%s] to av[%s]", bv, av));
} else {
bv = BiliTool.toBv(Long.parseLong(av));
// logger.trace(String.format("converted av[%s] to bv[%s]", av, bv));
}
// build standard share links
final String linkPartParam = part==-1 ? "" : "?p="+part;
final String linkAv = "https://www.bilibili.com/video/av"+av + linkPartParam;
final String linkBv = "https://www.bilibili.com/video/BV"+bv + linkPartParam;
final String idAv = "av"+av;
final String idBv = "BV"+bv;
// logger.trace("built all data.");
// build share message element
List<InlineQueryUnit<?>> result = new ArrayList<>();
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_BILI_AV+av), TITLE_BILI_AV+av,
new InputTextMessageContent(String.format(SHARE_FORMAT_HTML, linkAv, idAv)).parseMode(ParseMode.HTML)
)));
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_BILI_BV+bv), TITLE_BILI_BV+bv,
new InputTextMessageContent(String.format(SHARE_FORMAT_HTML, linkBv, idBv)).parseMode(ParseMode.HTML)
)));
return result;
}
return null;
}
}

View File

@ -0,0 +1,50 @@
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 javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static cc.sukazyo.cono.morny.util.tgapi.formatting.NamedUtils.inlineIds;
public class ShareToolTwitter implements ITelegramQuery {
public static final String TITLE_VX = "[tweet] Share as VxTwitter";
public static final String TITLE_VX_COMBINED = "[tweet] Share as VxTwitter(combination)";
public static final String ID_PREFIX_VX = "[morny/share/twitter/vxtwi]";
public static final String ID_PREFIX_VX_COMBINED = "[morny/share/twitter/vxtwi_combine]";
public static final Pattern REGEX_TWEET_LINK = Pattern.compile(
"^(?:https?://)?((?:(?:c\\.)?vx|fx|www\\.)?twitter\\.com)/((\\w+)/status/(\\d+)(?:/photo/(\\d+))?)/?(\\?[\\w&=-]+)?$");
@Nullable
@Override
public List<InlineQueryUnit<?>> query (@Nonnull Update event) {
if (event.inlineQuery().query() == null) return null;
final Matcher regex = REGEX_TWEET_LINK.matcher(event.inlineQuery().query());
if (regex.matches()) {
List<InlineQueryUnit<?>> result = new ArrayList<>();
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_VX+event.inlineQuery().query()), TITLE_VX,
String.format("https://vxtwitter.com/%s", regex.group(2))
)));
result.add(new InlineQueryUnit<>(new InlineQueryResultArticle(
inlineIds(ID_PREFIX_VX_COMBINED+event.inlineQuery().query()), TITLE_VX_COMBINED,
String.format("https://c.vxtwitter.com/%s", regex.group(2))
)));
return result;
}
return null;
}
}

View File

@ -1,7 +1,7 @@
package cc.sukazyo.cono.morny.daemon; package cc.sukazyo.cono.morny.daemon;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.util.CommonFormatUtils; import cc.sukazyo.cono.morny.util.CommonFormat;
import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.MessageEntity; import com.pengrad.telegrambot.model.MessageEntity;
import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.model.request.ParseMode;
@ -54,7 +54,7 @@ public class MedicationTimer extends Thread {
public void refreshNotificationWrite (Message edited) { public void refreshNotificationWrite (Message edited) {
if (edited.messageId() != lastNotify) return; if (edited.messageId() != lastNotify) return;
final String editTime = CommonFormatUtils.formatDate(edited.editDate()*1000, 8); final String editTime = CommonFormat.formatDate(edited.editDate()*1000, 8);
ArrayList<MessageEntity> entities = new ArrayList<>(); ArrayList<MessageEntity> entities = new ArrayList<>();
if (edited.entities() != null) entities.addAll(List.of(edited.entities())); if (edited.entities() != null) entities.addAll(List.of(edited.entities()));
entities.add(new MessageEntity(MessageEntity.Type.italic, edited.text().length() + "\n-- ".length(), editTime.length())); entities.add(new MessageEntity(MessageEntity.Type.italic, edited.text().length() + "\n-- ".length(), editTime.length()));

View File

@ -8,7 +8,7 @@ public class MornyDaemons {
public static void start () { public static void start () {
logger.info("ALL Morny Daemons starting..."); logger.info("ALL Morny Daemons starting...");
TrackerDataManager.init(); // TrackerDataManager.init();
medicationTimerInstance.start(); medicationTimerInstance.start();
logger.info("Morny Daemons started."); logger.info("Morny Daemons started.");
} }
@ -17,10 +17,10 @@ public class MornyDaemons {
logger.info("ALL Morny Daemons stopping..."); logger.info("ALL Morny Daemons stopping...");
TrackerDataManager.DAEMON.interrupt(); // TrackerDataManager.DAEMON.interrupt();
medicationTimerInstance.interrupt(); medicationTimerInstance.interrupt();
TrackerDataManager.trackingLock.lock(); // TrackerDataManager.trackingLock.lock();
try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); } try { medicationTimerInstance.join(); } catch (InterruptedException e) { e.printStackTrace(System.out); }
logger.info("ALL Morny Daemons STOPPED."); logger.info("ALL Morny Daemons STOPPED.");

View File

@ -1,6 +1,7 @@
package cc.sukazyo.cono.morny.data; package cc.sukazyo.cono.morny.data;
import cc.sukazyo.cono.morny.util.EncryptUtils; import cc.sukazyo.cono.morny.util.CommonConvert;
import cc.sukazyo.cono.morny.util.CommonEncrypt;
import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.User;
/** /**
@ -39,7 +40,7 @@ public class MornyJrrp {
* @return 算法得到的 jrrp 取值为 {@code [0.00. 100.00]} * @return 算法得到的 jrrp 取值为 {@code [0.00. 100.00]}
*/ */
public static double calcJrrpXmomi (long userId, long dayStamp) { public static double calcJrrpXmomi (long userId, long dayStamp) {
return (double)Long.parseLong(EncryptUtils.encryptByMD5(userId + "@" + dayStamp).substring(0, 4), 16) / (double)0xffff; return (double)Long.parseLong(CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(userId + "@" + dayStamp)).substring(0, 4), 16) / (double)0xffff;
} }
} }

View File

@ -1,5 +1,12 @@
package cc.sukazyo.cono.morny.data; package cc.sukazyo.cono.morny.data;
import cc.sukazyo.cono.morny.util.tgapi.ExtraAction;
import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.request.SendSticker;
import com.pengrad.telegrambot.response.SendResponse;
import java.lang.reflect.Field;
/** /**
* 存放 bot 使用到的贴纸 * 存放 bot 使用到的贴纸
* @since 0.4.2.0 * @since 0.4.2.0
@ -15,5 +22,47 @@ public class TelegramStickers {
public static final String ID_SENT = "CAACAgEAAx0CSQh32gABA--zYbiyU_wOijEitp-0tSl_k7W6l3gAAgMmAAJ4_MYF4GrompjXPx4jBA"; public static final String ID_SENT = "CAACAgEAAx0CSQh32gABA--zYbiyU_wOijEitp-0tSl_k7W6l3gAAgMmAAJ4_MYF4GrompjXPx4jBA";
public static final String ID_SAVED = "CAACAgEAAx0CSQh32gABBExuYdB_G0srfhQldRWkBYxWzCOv4-IAApooAAJ4_MYFcjuNZszfQcQjBA"; public static final String ID_SAVED = "CAACAgEAAx0CSQh32gABBExuYdB_G0srfhQldRWkBYxWzCOv4-IAApooAAJ4_MYFcjuNZszfQcQjBA";
public static final String ID_PROGYNOVA = "CAACAgUAAxkBAAICm2KEuL7UQqNP7vSPCg2DHJIND6UsAAKLAwACH4WSBszIo722aQ3jJAQ"; 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) {
for (Field object : TelegramStickers.class.getFields()) {
if (object.getType()==String.class && object.getName().startsWith("ID_")) {
try {
final String stickerId = (String)object.get("");
SendSticker echo = new SendSticker(sentChat, stickerId);
SendMessage echoName = new SendMessage(sentChat, object.getName());
if (replyToMessageId!=-1) echo.replyToMessageId(replyToMessageId);
SendResponse echoedName = actionObject.exec(echoName);
actionObject.exec(echo.replyToMessageId(echoedName.message().messageId()));
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
public static void echoStickerByID (String stickerFieldID, ExtraAction actionObject, long sentChat, int replyToMessageId) {
try {
// normally get the sticker and echo
Field sticker = TelegramStickers.class.getField(stickerFieldID);
SendMessage echoName = new SendMessage(sentChat, sticker.getName());
SendSticker echo = new SendSticker(sentChat, (String)sticker.get(""));
if (replyToMessageId!=-1) echo.replyToMessageId(replyToMessageId);
SendResponse echoedName = actionObject.exec(echoName);
actionObject.exec(echo.replyToMessageId(echoedName.message().messageId()));
} catch (NoSuchFieldException e) {
// no such sticker found
SendSticker echo404 = new SendSticker(sentChat, TelegramStickers.ID_404);
if (replyToMessageId!=-1) echo404.replyToMessageId(replyToMessageId);
actionObject.exec(echo404);
} catch (IllegalAccessException e) {
// java-reflect get sticker FILE_ID failed
throw new RuntimeException(e);
}
}
} }

View File

@ -0,0 +1,73 @@
package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
public class BiliTool {
private static final long V_CONV_XOR = 177451812L;
private static final long V_CONV_ADD = 8728348608L;
private static final char[] BV_TABLE = "fZodR9XQDSUm21yCkr6zBqiveYah8bt4xsWpHnJE7jL5VG3guMTKNPAwcF".toCharArray();
private static final int TABLE_INT = BV_TABLE.length;
private static final Map<Character, Integer> BV_TABLE_REVERSED = new HashMap<>();
static { for (int i = 0; i < BV_TABLE.length; i++) BV_TABLE_REVERSED.put(BV_TABLE[i], i); }
private static final char[] BV_TEMPLATE = "1 4 1 7 ".toCharArray();
private static final int[] BV_TEMPLATE_FILTER = new int[]{9, 8, 1, 6, 2, 4};
/**
* Convert a <a href="https://www.bilibili.com/">Bilibili</a> AV video id format to BV id format.
* <p>
* the AV id is a number; the BV id is a special base58 number, it shows as String in programming.<br>
* eg:<br>
* while the link <i>{@code https://www.bilibili.com/video/BV17x411w7KC/}</i>
* shows the same with <i>{@code https://www.bilibili.com/video/av170001/}</i>,
* the AV id is <u>{@code 170001}</u>, the BV id is <u>{@code 17x411w7KC}</u>
* <p>
* for now , the BV id has 10 digits.
* the method <b>available while the <u>av-id < 2^27</u></b>, while it theoretically available when the av-id < 2^30.
*
* @see <a href="https://www.zhihu.com/question/381784377/answer/1099438784">mcfx的回复: 如何看待 2020 3 23 日哔哩哔哩将稿件的av 变更为BV </a>
*
* @param bv the BV id, a string in (a special) base58 number format, <b>without "BV" prefix</b>.
* @return the AV id corresponding to this bv id in <a href="https://www.bilibili.com/">Bilibili</a>, formatted as a number.
*/
@Nonnegative
public static long toAv (@Nonnull String bv) {
long av = 0;
for (int i = 0; i < BV_TEMPLATE_FILTER.length; i++) {
av += BV_TABLE_REVERSED.get(bv.charAt(BV_TEMPLATE_FILTER[i])) * Math.pow(TABLE_INT,i);
}
return (av-V_CONV_ADD)^V_CONV_XOR;
}
/**
* Convert a <a href="https://www.bilibili.com/">Bilibili</a> BV video id format to AV id format.
* <p>
* the AV id is a number; the BV id is a special base58 number, it shows as String in programming.<br>
* eg:<br>
* while the link <i>{@code https://www.bilibili.com/video/BV17x411w7KC/}</i>
* shows the same with <i>{@code https://www.bilibili.com/video/av170001/}</i>,
* the AV id is <u>{@code 170001}</u>, the BV id is <u>{@code 17x411w7KC}</u>
* <p>
* for now , the BV id has 10 digits.
* the method <b>available while the <u>av-id < 2^27</u></b>, while it theoretically available when the av-id < 2^30.
*
* @see <a href="https://www.zhihu.com/question/381784377/answer/1099438784">mcfx的回复: 如何看待 2020 3 23 日哔哩哔哩将稿件的av 变更为BV </a>
*
* @param av the (base10) AV id.
* @return the AV id corresponding to this bv id in <a href="https://www.bilibili.com/">Bilibili</a>,
* as a (special) base 58 number format <b>without "BV" prefix</b>.
*/
@Nonnull
public static String toBv (@Nonnegative long av) {
av = (av^V_CONV_XOR)+V_CONV_ADD;
final char[] bv = BV_TEMPLATE.clone();
for (int i = 0; i < BV_TEMPLATE_FILTER.length; i++) {
bv[BV_TEMPLATE_FILTER[i]] = BV_TABLE[(int)(Math.floor(av/(Math.pow(TABLE_INT, i)))%TABLE_INT)];
}
return String.copyValueOf(bv);
}
}

View File

@ -0,0 +1,61 @@
package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
/**
* 进行简单类型转换等工作的类.
*/
public class CommonConvert {
/**
* 将字节数组转换成 hex 字符串.
* @param b 字节数组
* @return String 格式的字节数组的 hex 每个字节当中没有分隔符
* @see #byteToHex(byte)
*/
@Nonnull
public static String byteArrayToHex(@Nonnull byte[] b){
StringBuilder sb = new StringBuilder();
for (byte value : b) {
sb.append(byteToHex(value));
}
return sb.toString();
}
/**
* 将一个字节转换成十六进制 hex 字符串.
* @param b 字节值
* @return String 格式的字节的 hex 小写
*/
@Nonnull
public static String byteToHex(byte b) {
final String hex = Integer.toHexString(b & 0xff);
return hex.length()<2?"0"+hex:hex;
}
/**
* 将一个字符串数组按照一定规则连接.
* <p>
* 连接的方式类似于"数据1+分隔符+数据2+分隔符+...+数据n-1+分隔符+数据n"
*
* @param array 需要进行连接的字符串数组数组中每一个元素会是一个数据
* @param connector 在每两个传入数据中插入的分隔符
* @param startIndex 从传入的数据组中的哪一个位置开始第一个元素的位置是 {@code 0}
* @param stopIndex 从传入的数据组中的哪一个位置停止元素位置计算方式同上
* @return 连接好的字符串
*/
@Nonnull
public static String stringsConnecting (
@Nonnull String[] array, @Nonnull String connector, @Nonnegative int startIndex, @Nonnegative int stopIndex
) {
final StringBuilder builder = new StringBuilder();
for (int i = startIndex; i < stopIndex; i++) {
builder.append(array[i]);
builder.append(connector);
}
builder.append(array[stopIndex]);
return builder.toString();
}
}

View File

@ -0,0 +1,144 @@
package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnull;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
/**
* 用于数据加密或编解码的工具类.
* <p>
* 出于 java std Base64 {@link Base64.Encoder encode}/{@link Base64.Decoder decode} 十分好用在此不再进行包装
*/
public class CommonEncrypt {
/**
* 在使用加密算法处理字符串时默认会使用的字符串编码.
* <p>
* Morny 使用 UTF-8 编码因为这是一般而言加解密工具的默认行为
*/
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 散列值.
*
* @param data byte 数组形式的数据体
* @return 二进制(byte数组)格式的数据的 md5 散列值
*/
@Nonnull
public static byte[] hashMd5 (@Nonnull byte[] data) {
return hashAsJavaMessageDigest("md5", data);
}
/**
* 取得一个字符串的 md5 散列值.
* <p>
* 输入的字符串将会以 {@link #ENCRYPT_STANDARD_CHARSET 默认的 UTF-8} 编码进行解析
*
* @param originString 要进行散列的字符串
* @return 二进制(byte数组)格式的 md5 散列值
*/
@Nonnull
public static byte[] hashMd5 (String originString) {
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 散列值.
* <p>
* 输入的字符串将会以 {@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 散列值.
* <p>
* 输入的字符串将会以 {@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 散列值.
* <p>
* 输入的字符串将会以 {@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));
}
@Nonnull
public static String base64FilenameLint (String inputName) {
if (inputName.endsWith(".b64")) {
return inputName.substring(0, inputName.length()-".b64".length());
} else if (inputName.endsWith(".b64.txt")) {
return inputName.substring(0, inputName.length()-".b64.txt".length());
} else if (inputName.endsWith(".base64")) {
return inputName.substring(0, inputName.length()-".base64".length());
} else if (inputName.endsWith(".base64.txt")) {
return inputName.substring(0, inputName.length()-".base64.txt".length());
} else {
return inputName;
}
}
}

View File

@ -6,10 +6,12 @@ import java.time.ZoneId;
import java.time.ZoneOffset; import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatter;
public class CommonFormatUtils { public class CommonFormat {
public static final String DATE_TIME_PATTERN_FULL_MILLIS = "yyyy-MM-dd HH:mm:ss:SSS";
public static String formatDate (long timestamp, int utcOffset) { public static String formatDate (long timestamp, int utcOffset) {
return DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant( return DateTimeFormatter.ofPattern(DATE_TIME_PATTERN_FULL_MILLIS).format(LocalDateTime.ofInstant(
Instant.ofEpochMilli(timestamp), Instant.ofEpochMilli(timestamp),
ZoneId.ofOffset("UTC", ZoneOffset.ofHours(utcOffset)) ZoneId.ofOffset("UTC", ZoneOffset.ofHours(utcOffset))
)); ));

View File

@ -1,62 +0,0 @@
package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnull;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
/**
* 用于数据加密或编码的工具类<br>
* <s>显然大部分代码是抄来的</s><br>
* <ul>
* <li><a href="https://blog.csdn.net/yu540135101/article/details/86765457">{@link #encryptByMD5} & {@link #byteToHex}
* & {@link #byteArrayToHex} 来源</a></li>
* </ul>
*/
public class EncryptUtils {
private final static String[] hexArray = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f"};
/***
* 对指定的字符串进行MD5加密
*/
@Nonnull
public static String encryptByMD5(String originString) {
try {
//创建具有MD5算法的信息摘要
MessageDigest md = MessageDigest.getInstance("MD5");
//使用指定的字节数组对摘要进行最后更新然后完成摘要计算
byte[] bytes = md.digest(originString.getBytes());
//将得到的字节数组变成字符串返回
String s = byteArrayToHex(bytes);
return s.toUpperCase();
} catch (NoSuchAlgorithmException e) {
throw new IllegalStateException();
}
}
/**
* 将字节数组转换成十六进制并以字符串的形式返回
* 128位是指二进制位二进制太长所以一般都改写成16进制
* 每一位16进制数可以代替4位二进制数所以128位二进制数写成16进制就变成了128/4=32位
*/
private static String byteArrayToHex(byte[] b){
StringBuilder sb = new StringBuilder();
for (byte value : b) {
sb.append(byteToHex(value));
}
return sb.toString();
}
/**
* 将一个字节转换成十六进制并以字符串的形式返回
*/
public static String byteToHex(byte b) {
int n = b;
if (n < 0)
n = n + 256;
int d1 = n / 16;
int d2 = n % 16;
return hexArray[d1]+hexArray[d2];
}
}

View File

@ -0,0 +1,47 @@
package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnull;
import java.util.ArrayList;
public class UniversalCommand {
@Nonnull
public static String[] format (@Nonnull String com) {
final ArrayList<String> arr = new ArrayList<>();
final StringBuilder tmp = new StringBuilder();
final char[] coma = com.toCharArray();
for (int i = 0; i < coma.length; i++) {
if (coma[i] == ' ') {
if (!tmp.toString().equals("")) { arr.add(tmp.toString()); }
tmp.setLength(0);
} else if (coma[i] == '"') {
while (true) {
i++;
if (coma[i] == '"') {
break;
} else if (coma[i] == '\\' && (coma[i+1] == '"' || coma[i+1] == '\\')) {
i++;
tmp.append(coma[i]);
} else {
tmp.append(coma[i]);
}
}
} else if (coma[i] == '\\' && (coma[i+1] == ' ' || coma[i+1] == '"' || coma[i+1] == '\\')) {
i++;
tmp.append(coma[i]);
} else {
tmp.append(coma[i]);
}
}
if (!tmp.toString().equals("")) { arr.add(tmp.toString()); }
tmp.setLength(0);
final String[] out = new String[arr.size()];
arr.toArray(out);
return out;
}
}

View File

@ -0,0 +1,88 @@
package cc.sukazyo.cono.morny.util.tgapi;
import cc.sukazyo.cono.morny.util.tgapi.event.EventRuntimeException;
import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.ChatMember;
import com.pengrad.telegrambot.model.User;
import com.pengrad.telegrambot.request.BaseRequest;
import com.pengrad.telegrambot.request.GetChatMember;
import com.pengrad.telegrambot.response.BaseResponse;
public class ExtraAction {
private final TelegramBot bot;
public ExtraAction (TelegramBot bot) {
this.bot = bot;
}
public static ExtraAction as (TelegramBot bot) {
return new ExtraAction(bot);
}
public boolean isUserInGroup (User user, Chat chat) {
return isUserInGroup(user.id(), chat.id());
}
public <T extends BaseRequest<T, R>, R extends BaseResponse> R exec (T req) {
return exec(req, "");
}
public <T extends BaseRequest<T, R>, R extends BaseResponse> R exec (T req, String errorMessage) {
final R resp = bot.execute(req);
if (!resp.isOk()) throw new EventRuntimeException.ActionFailed(
(errorMessage.equals("") ? String.valueOf(resp.errorCode()) : errorMessage),
resp
);
return resp;
}
public boolean isUserInGroup (User user, Chat chat, ChatMember.Status permissionLevel) {
return isUserInGroup(user.id(), chat.id(), permissionLevel);
}
public boolean isUserInGroup (long userId, long chatId) {
return isUserInGroup(userId, chatId, ChatMember.Status.restricted);
}
public boolean isUserInGroup (long userId, long chatId, ChatMember.Status permissionLevel) {
final ChatMember chatMember = exec(new GetChatMember(chatId, userId)).chatMember();
return
chatMember != null &&
UserPermissionLevel.as(chatMember.status()).hasPermission(UserPermissionLevel.as(permissionLevel));
}
}
enum UserPermissionLevel {
CREATOR(3),
ADMINISTRATOR(2),
MEMBER(1),
RESTRICTED(0),
LEFT(-1),
KICKED(-2);
final int permissionLevel;
UserPermissionLevel (int permissionLevel) {
this.permissionLevel = permissionLevel;
}
static UserPermissionLevel as (ChatMember.Status status) {
return switch (status) {
case creator -> CREATOR;
case administrator -> ADMINISTRATOR;
case member -> MEMBER;
case restricted -> RESTRICTED;
case left -> LEFT;
case kicked -> KICKED;
};
}
boolean hasPermission (UserPermissionLevel required) {
return this.permissionLevel >= required.permissionLevel;
}
}

View File

@ -0,0 +1,66 @@
package cc.sukazyo.cono.morny.util.tgapi;
import cc.sukazyo.cono.morny.util.UniversalCommand;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
public class InputCommand {
private final String target;
private final String command;
private final String[] args;
private InputCommand (@Nullable String target, @Nonnull String command, @Nonnull String[] args) {
this.target = target;
this.command = command;
this.args = args;
}
public InputCommand (@Nonnull String[] inputArray) {
this(parseInputArray(inputArray));
}
public InputCommand (@Nonnull String input) {
this(UniversalCommand.format(input));
}
public InputCommand (@Nonnull InputCommand source) {
this(source.target, source.command, source.args);
}
public static InputCommand parseInputArray (@Nonnull String[] inputArray) {
final String[] cx = inputArray[0].split("@", 2);
final String[] args = new String[inputArray.length-1];
System.arraycopy(inputArray, 1, args, 0, inputArray.length - 1);
return new InputCommand(cx.length == 1 ? null : cx[1], cx[0], args);
}
@Nullable
public String getTarget () {
return target;
}
@Nonnull
public String getCommand () {
return command;
}
@Nonnull
public String[] getArgs () {
return args;
}
public boolean hasArgs () {
return args.length != 0;
}
@Override
@Nonnull
public String toString() {
return String.format("{{%s}@{%s}#{%s}}", command, target, Arrays.toString(args));
}
}

View File

@ -0,0 +1,35 @@
package cc.sukazyo.cono.morny.util.tgapi.event;
import com.pengrad.telegrambot.response.BaseResponse;
public class EventRuntimeException extends RuntimeException {
public EventRuntimeException () {
super();
}
public EventRuntimeException (String message) {
super(message);
}
public static class ActionFailed extends EventRuntimeException {
private final BaseResponse response;
public ActionFailed (BaseResponse response) {
super();
this.response = response;
}
public ActionFailed (String message, BaseResponse response) {
super(message);
this.response = response;
}
public BaseResponse getResponse() {
return response;
}
}
}

View File

@ -0,0 +1,15 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import javax.annotation.Nonnull;
public class MsgEscape {
@Nonnull
public static String escapeHtml (@Nonnull String raw) {
raw = raw.replaceAll("&", "&amp;");
raw = raw.replaceAll("<", "&lt;");
raw = raw.replaceAll(">", "&gt;");
return raw;
}
}

View File

@ -0,0 +1,18 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import cc.sukazyo.cono.morny.util.CommonConvert;
import cc.sukazyo.cono.morny.util.CommonEncrypt;
import javax.annotation.Nonnull;
public class NamedUtils {
public static String inlineIds (@Nonnull String tag) {
return inlineIds(tag, "");
}
public static String inlineIds (@Nonnull String tag, @Nonnull String taggedData) {
return CommonConvert.byteArrayToHex(CommonEncrypt.hashMd5(tag+taggedData));
}
}

View File

@ -0,0 +1,21 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.Chat;
import com.pengrad.telegrambot.model.Message;
import com.pengrad.telegrambot.model.User;
public class TGToString {
public static TGToStringFromChat as (Chat chat) {
return new TGToStringFromChat(chat);
}
public static TGToStringFromUser as (User user) {
return new TGToStringFromUser(user);
}
public static TGToStringFromMessage as (Message message) {
return new TGToStringFromMessage(message);
}
}

View File

@ -0,0 +1,22 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.Chat;
public class TGToStringFromChat {
private final Chat data;
public TGToStringFromChat(Chat chat) {
this.data = chat;
}
public String toStringFullNameId() {
if (data.title() == null) {
throw new IllegalArgumentException("Cannot format private chat to group Name+Id format.");
}
return (data.username() == null) ?
(String.format("%s [%d]", data.title(), data.id())) :
(String.format("%s {%s}[%d]", data.title(), data.username(), data.id()));
}
}

View File

@ -1,7 +1,5 @@
package cc.sukazyo.cono.morny.util.tgapi; package cc.sukazyo.cono.morny.util.tgapi.formatting;
import cc.sukazyo.untitled.telegram.api.formatting.TGToString;
import cc.sukazyo.untitled.util.telegram.formatting.MsgEscape;
import com.pengrad.telegrambot.model.Message; import com.pengrad.telegrambot.model.Message;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -12,7 +10,6 @@ public class TGToStringFromMessage extends TGToString {
private final Message message; private final Message message;
public TGToStringFromMessage (@Nonnull Message message) { this.message = message; } public TGToStringFromMessage (@Nonnull Message message) { this.message = message; }
public static TGToStringFromMessage as (@Nonnull Message message) { return new TGToStringFromMessage(message); }
@Nonnull @Nonnull
public String getSenderFirstNameRefHtml () { public String getSenderFirstNameRefHtml () {

View File

@ -0,0 +1,53 @@
package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.User;
public class TGToStringFromUser {
private final User data;
public TGToStringFromUser (User user) {
this.data = user;
}
public String fullname () {
return data.firstName() + (data.lastName()==null ? "" : " "+data.lastName());
}
public String fullnameRefHtml () {
return String.format(
"<a href='tg://user?id=%d'>%s</a>",
data.id(),
MsgEscape.escapeHtml(fullname())
);
}
public String fullnameRefMarkdown () {
return String.format(
"[%s](tg://user?id=%d)",
fullname(),
data.id()
);
}
public String firstnameRefHtml () {
return String.format(
"<a href='tg://user?id=%d'>%s</a>",
data.id(),
MsgEscape.escapeHtml(data.firstName())
);
}
public String firstnameRefMarkdown () {
return String.format(
"[%s](tg://user?id=%d)",
data.firstName(),
data.id()
);
}
public String toStringLogTag () {
return (data.username()==null ? fullname()+" " : "@"+data.username()) + "[" + data.id() + "]";
}
}

View File

@ -1,4 +1,4 @@
package cc.sukazyo.cono.morny.util; package cc.sukazyo.cono.morny.util.tgapi.formatting;
import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.User;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
@ -11,7 +11,7 @@ import java.io.IOException;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import static cc.sukazyo.untitled.util.telegram.formatting.MsgEscape.escapeHtml; import static cc.sukazyo.cono.morny.util.tgapi.formatting.MsgEscape.escapeHtml;
public class TelegramUserInformation { public class TelegramUserInformation {

View File

@ -0,0 +1,18 @@
package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.util.UniversalCommand;
import java.util.*;
public class MornyCLI {
public static void main (String[] args) {
Scanner line = new Scanner(System.in);
System.out.print("$ java -jar morny-coeur-"+GradleProjectConfigures.VERSION+".jar " );
String x = line.nextLine();
ServerMain.main(UniversalCommand.format(x));
}
}

View File

@ -0,0 +1,30 @@
package cc.sukazyo.cono.morny.util;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static cc.sukazyo.cono.morny.util.BiliTool.*;
public class TestBiliTool {
private static final String AV_BV_DATA_CSV = """
17x411w7KC, 170001
1Q541167Qg, 455017605
1mK4y1C7Bz, 882584971
1T24y197V2, 688730800
""";
@ParameterizedTest
@CsvSource(textBlock = AV_BV_DATA_CSV)
void testAvToBv (String bv, int av) {
Assertions.assertEquals(bv, toBv(av));
}
@ParameterizedTest
@CsvSource(textBlock = AV_BV_DATA_CSV)
void testBvToAv (String bv, int av) {
Assertions.assertEquals(av, toAv(bv));
}
}

View File

@ -0,0 +1,50 @@
package cc.sukazyo.cono.morny.util;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.MethodSource;
import java.util.stream.Stream;
import static cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex;
import static cc.sukazyo.cono.morny.util.CommonConvert.byteToHex;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.params.provider.Arguments.arguments;
public class TestCommonConvert {
@ParameterizedTest
@CsvSource(textBlock = """
0x00, 00
0x01, 01
0x20, 20
0x77, 77
-0x60, a0
0x0a, 0a
-0x01, ff
-0x05, fb
"""
)
void testByteToHex(byte source, String expected) {
assertEquals(expected, byteToHex(source));
}
public static Stream<Arguments> testByteArrayToHexProvider () {
return Stream.of(
arguments(new byte[]{0x00}, "00"),
arguments(new byte[]{(byte)0xff}, "ff"),
arguments(new byte[]{(byte)0xc3}, "c3"),
arguments(new byte[]{}, ""),
arguments(new byte[]{0x30,0x0a,0x00,0x04,(byte)0xb0,0x00}, "300a0004b000"),
arguments(new byte[]{0x00,0x00,0x0a,(byte)0xff,(byte)0xfc,(byte)0xab,(byte)0x00,0x04}, "00000afffcab0004"),
arguments(new byte[]{0x00,0x7c,0x11,0x28,(byte)0x88,(byte)0xa6,(byte)0xfc,0x30}, "007c112888a6fc30")
);
}
@ParameterizedTest
@MethodSource("testByteArrayToHexProvider")
void testByteArrayToHex (byte[] raw, String expected) {
assertEquals(expected, byteArrayToHex(raw));
}
}

View File

@ -0,0 +1,23 @@
package cc.sukazyo.cono.morny.util;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static cc.sukazyo.cono.morny.util.CommonConvert.byteArrayToHex;
import static cc.sukazyo.cono.morny.util.CommonEncrypt.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestCommonEncrypt {
@ParameterizedTest
@SuppressWarnings("UnnecessaryStringEscape")
@CsvSource(textBlock = """
28be57d368b75051da76c068a6733284, '莲子'
9644c5cbae223013228cd528817ba4f5, '莲子\n'
d41d8cd98f00b204e9800998ecf8427e, ''
""")
void testHashMd5_String (String md5, String text) {
assertEquals(md5, byteArrayToHex(hashMd5(text)));
}
}

View File

@ -0,0 +1,36 @@
package cc.sukazyo.cono.morny.util;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import static cc.sukazyo.cono.morny.util.CommonFormat.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
public class TestCommonFormat {
@ParameterizedTest
@CsvSource(textBlock = """
1664646870402, 8, 2022-10-02 01:54:30:402
1, 8, 1970-01-01 08:00:00:001
0, -1, 1969-12-31 23:00:00:000
"""
)
void testFormatDate (long timestamp, int utfOffset, String expectedHumanReadableTime) {
assertEquals(expectedHumanReadableTime, formatDate(timestamp, utfOffset));
}
@ParameterizedTest
@CsvSource(textBlock = """
100, '100ms'
3000, '3s 0ms'
326117522, '3d 18h 35min 17s 522ms'
53373805, 14h 49min 33s 805ms
""")
// -1, '-1ms' // WARN: maybe sometime an unexpected usage
// -194271974291, '-291ms' //
// """) //
void testFormatDuration (long durationMillis, String humanReadableDuration) {
assertEquals(humanReadableDuration, formatDuration(durationMillis));
}
}