Merge tag '0.3.4.4' into release

This commit is contained in:
A.C.Sukazyo Eyre 2021-12-24 18:30:38 +08:00
commit 868542cd4e
Signed by: Eyre_S
GPG Key ID: EFB47D98FE082FAD
17 changed files with 468 additions and 122 deletions

View File

@ -1,12 +1,13 @@
plugins { plugins {
id 'java' id 'java'
id 'java-library'
id 'maven-publish' id 'maven-publish'
id 'application' id 'application'
id 'com.github.johnrengelman.shadow' version '7.1.0' id 'com.github.johnrengelman.shadow' version '7.1.0'
} }
group 'cc.sukazyo' group 'cc.sukazyo'
version '0.3.3' version '0.3.4.4'
project.ext.archiveBaseName = 'Coeur_Morny_Cono' project.ext.archiveBaseName = 'Coeur_Morny_Cono'
project.ext.artifactId = 'morny-coeur' project.ext.artifactId = 'morny-coeur'
mainClassName = 'cc.sukazyo.cono.morny.MornyCoeur' mainClassName = 'cc.sukazyo.cono.morny.MornyCoeur'
@ -17,25 +18,37 @@ repositories {
dependencies { dependencies {
implementation 'com.github.pengrad:java-telegram-bot-api:5.3.0' compileOnlyApi "com.github.spotbugs:spotbugs-annotations:4.5.0"
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.1' implementation 'com.github.pengrad:java-telegram-bot-api:5.4.0'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1'
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2'
} }
compileJava.doFirst { task updateVersionCode {
ant.replaceregexp(match:'VERSION = ["a-zA-Z0-9.\\-_+@]+;', replace:"VERSION = \"$project.version\";", flags:'g', byline:true) { ant.replaceregexp(match:'VERSION = ["a-zA-Z0-9.\\-_+@]+;', replace:"VERSION = \"$project.version\";", flags:'g', byline:true) {
fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'MornySystem.java') fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'GradleProjectConfigures.java')
}
ant.replaceregexp(match:'COMPILE_TIMESTAMP = [0-9]+L;', replace:"COMPILE_TIMESTAMP = ${System.currentTimeMillis()}L;", flags:'g', byline:true) {
fileset(dir: 'src/main/java/cc/sukazyo/cono/morny', includes: 'GradleProjectConfigures.java')
} }
} }
compileJava.dependsOn updateVersionCode
test { test {
useJUnitPlatform() useJUnitPlatform()
} }
java { java {
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
withSourcesJar() withSourcesJar()
} }
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {

View File

@ -0,0 +1,9 @@
package cc.sukazyo.cono.morny;
/**
* the final field that will be updated by gradle automatically.
*/
public class GradleProjectConfigures {
public static final String VERSION = "0.3.4.4";
public static final long COMPILE_TIMESTAMP = 1638884621273L;
}

View File

@ -2,30 +2,78 @@ package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.util.StringUtils; import cc.sukazyo.cono.morny.util.StringUtils;
import javax.annotation.Nonnull;
/**
* Morny 的简单控制台 log 记录器
*/
public class Logger { public class Logger {
/** Morny 的控制台 logger 实例 */
public static final Logger logger = new Logger(); public static final Logger logger = new Logger();
public void info(String message) { /**
* [INFO] 级别的 log 消息
* @see #formatMessage(String, String) 输出整理规则
* @param message 消息文本支持多行
*/
public void info(@Nonnull String message) {
System.out.println(formatMessage(message, "INFO")); System.out.println(formatMessage(message, "INFO"));
} }
public void warn (String message) { /**
waring(message); * [WARN] 级别的 log 消息<br>
* {@link #warning(String)} 的重写
* @see #formatMessage(String, String) 输出整理规则
* @see #warning(String) 源方法
* @param message 消息文本支持多行
*/
public void warn (@Nonnull String message) {
warning(message);
} }
public void waring (String message) { /**
* [WARN] 级别的 log 消息
* @see #formatMessage(String, String) 输出整理规则
* @see #warn(String) 别名:warn
* @param message 消息文本支持多行
*/
public void warning (@Nonnull String message) {
System.out.println(formatMessage(message, "WARN")); System.out.println(formatMessage(message, "WARN"));
} }
public void error (String message) { /**
* [ERRO] 级别的 log 消息
* @see #formatMessage(String, String) 输出整理规则
* @param message 消息文本支持多行
*/
public void error (@Nonnull String message) {
System.out.println(formatMessage(message, "ERRO")); System.out.println(formatMessage(message, "ERRO"));
} }
private String formatMessage (String message, String level) { /**
String prompt = String.format("[%s][%s]", System.currentTimeMillis(), Thread.currentThread().getName()); * 将传入的消息和消息元数据重新整理为固定格式<br>
String levelStr = String.format("[%s]", level); * <br>
String newline = "\n" + StringUtils.repeatChar('\'', prompt.length()) + levelStr; * 这个方法会{@link System#currentTimeMillis() 获取当前时间}{@link Thread#currentThread() 当前线程}的名称
* 然后将数据整理为 {@code [<timestamp>][<thread-name>][<level>]<data>} 格式<br>
* 如果消息是多行的则每行的开头都会被加入 {@code [<timestamp>][<thread-name>][<level>]} 这样的前缀
* 不过前缀中 {@code [<timestamp>][<thread-name>]} 会被转换为等长的 {@code '} 字串
* <br>
* 最终的 format 格式将会类似于以下的模样<pre><code>
[1019284827][EVT388223][INFO]Something message got:
'''''''''''''''''''''''[INFO] - data source: 19773
'''''''''''''''''''''''[INFO] - message raw: noh2q0jwd9j-jn-9jq92-ed
* </code></pre>
*
* @param message 消息文本支持多行
* @param level log级别考虑到对齐推荐使用四位窄字元
* @return 整理后的字符串
*/
@Nonnull
private String formatMessage (@Nonnull String message, @Nonnull String level) {
final String prompt = String.format("[%s][%s]", System.currentTimeMillis(), Thread.currentThread().getName());
final String levelStr = String.format("[%s]", level);
final String newline = "\n" + StringUtils.repeatChar('\'', prompt.length()) + levelStr;
return prompt + levelStr + message.replaceAll("\\n", newline); return prompt + levelStr + message.replaceAll("\\n", newline);
} }

View File

@ -6,14 +6,67 @@ import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager;
import com.pengrad.telegrambot.TelegramBot; import com.pengrad.telegrambot.TelegramBot;
import com.pengrad.telegrambot.request.GetMe; import com.pengrad.telegrambot.request.GetMe;
import javax.annotation.Nonnull;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import static cc.sukazyo.cono.morny.Logger.logger; import static cc.sukazyo.cono.morny.Logger.logger;
/**
* Morny Cono 核心<br>
* <br>
* - 的程序化入口类保管着 morny 的核心属性<br>
*/
public class MornyCoeur { public class MornyCoeur {
/** morny 的 bot 账户 */
private static TelegramBot account; private static TelegramBot account;
public static final String USERNAME = "morny_cono_annie_bot"; /**
* morny bot 账户的用户名<br>
* <br>
* 出于技术限制这个字段目前是写死的
*/
public static String username;
public static void main (String[] args) { /**
* 程序入口<br>
* <br>
* 会从命令行参数取得初始化数据并初始化程序和bot<br>
* <br>
* - 第一个参数({@code args[0]})必序传递值为 telegram-bot api-token<br>
* - 第二个参数可选 {@code --no-hello} {@code --only-hello}
* 前者表示不输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语}
* 后者表示只输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语}而不运行程序逻辑<br>
* <br>
* 或者在第一个参数处使用 {@code --version} 来输出当前程序的版本信息
*
* @param args 程序命令行参数
*/
public static void main (@Nonnull String[] args) {
if ("--version".equals(args[0])) {
logger.info(String.format("""
Morny Cono Version
- version :
%s
- md5hash :
%s
- co.time :
%d
%s [UTC]""",
MornySystem.VERSION,
MornySystem.getJarMd5(),
GradleProjectConfigures.COMPILE_TIMESTAMP,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant(
Instant.ofEpochMilli(GradleProjectConfigures.COMPILE_TIMESTAMP),
ZoneId.ofOffset("UTC", ZoneOffset.UTC)))
));
return;
}
if (!(args.length > 1 && "--no-hello".equals(args[1]))) if (!(args.length > 1 && "--no-hello".equals(args[1])))
logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII); logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII);
@ -24,6 +77,10 @@ public class MornyCoeur {
configureSafeExit(); configureSafeExit();
logger.info("args key:\n " + args[0]); logger.info("args key:\n " + args[0]);
if (args.length > 2) {
username = args[2];
logger.info("login as:\n " + args[2]);
}
try { account = login(args[0]); } try { account = login(args[0]); }
catch (Exception e) { logger.error("Cannot login to bot/api. :\n " + e.getMessage()); System.exit(-1); } catch (Exception e) { logger.error("Cannot login to bot/api. :\n " + e.getMessage()); System.exit(-1); }
@ -38,24 +95,42 @@ public class MornyCoeur {
} }
/**
* 用于退出时进行缓存的任务处理等进行安全退出
*/
private static void exitCleanup () { private static void exitCleanup () {
TrackerDataManager.DAEMON.interrupt(); TrackerDataManager.DAEMON.interrupt();
TrackerDataManager.trackingLock.lock(); TrackerDataManager.trackingLock.lock();
} }
/**
* 为程序在虚拟机上添加退出钩子
*/
private static void configureSafeExit () { private static void configureSafeExit () {
Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup)); Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup));
} }
public static TelegramBot login (String key) { /**
TelegramBot account = new TelegramBot(key); * 登录 bot<br>
* <br>
* 会反复尝试三次进行登录如果登录失败则会直接抛出 RuntimeException 结束处理
* 会通过 GetMe 动作验证是否连接上了 telegram api 服务器
* 同时也要求登录获得的 username {@link #username} 声明值相等
*
* @param key bot api-token
* @return 成功登录后的 {@link TelegramBot} 对象
*/
@Nonnull
public static TelegramBot login (@Nonnull String key) {
final TelegramBot account = new TelegramBot(key);
logger.info("Trying to login..."); logger.info("Trying to login...");
for (int i = 1; i < 4; i++) { for (int i = 1; i < 4; i++) {
if (i != 1) logger.info("retrying..."); if (i != 1) logger.info("retrying...");
try { try {
String username = account.execute(new GetMe()).user().username(); final String username = account.execute(new GetMe()).user().username();
if (!USERNAME.equals(username)) if (username != null && !MornyCoeur.username.equals(username))
throw new RuntimeException("Required the bot @"+USERNAME + " but @"+username + " logged in!"); throw new RuntimeException("Required the bot @" + MornyCoeur.username + " but @" + username + " logged in!");
else MornyCoeur.username = username;
logger.info("Succeed login to @" + username); logger.info("Succeed login to @" + username);
return account; return account;
} catch (Exception e) { } catch (Exception e) {
@ -66,6 +141,11 @@ public class MornyCoeur {
throw new RuntimeException("Login failed.."); throw new RuntimeException("Login failed..");
} }
/**
* 获取登录成功后的 telegram bot 对象
*
* @return {@link #account MornyCoeur.account}
*/
public static TelegramBot getAccount () { public static TelegramBot getAccount () {
return account; return account;
} }

File diff suppressed because one or more lines are too long

View File

@ -2,12 +2,32 @@ package cc.sukazyo.cono.morny;
import cc.sukazyo.cono.morny.util.FileUtils; import cc.sukazyo.cono.morny.util.FileUtils;
import javax.annotation.Nonnull;
import java.net.URISyntaxException; import java.net.URISyntaxException;
/**
* Morny Cono Coeur 的程序属性存放类
*/
public class MornySystem { public class MornySystem {
public static final String VERSION = "0.3.3"; /**
* 程序的语义化版本号<br>
* 会由 gradle 任务 {@code updateVersionCode} 更新
*/
public static final String VERSION = GradleProjectConfigures.VERSION;
/**
* 获取程序 jar 文件的 md5-hash <br>
* <br>
* 只支持 jar 文件方式启动的程序
* 如果是通过 classpath 来启动则会返回找不到文件的错误数据<br>
* - 或许需要注意这种情况下会出现程序文件所在的路径<br>
* <br>
* 值格式为 {@link java.lang.String}
*
* @return 程序jar文件的 md5-hash 值字符串或错误信息
*/
@Nonnull
public static String getJarMd5() { public static String getJarMd5() {
try { try {
return FileUtils.getMD5Three(MornyCoeur.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); return FileUtils.getMD5Three(MornyCoeur.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath());

View File

@ -3,12 +3,29 @@ package cc.sukazyo.cono.morny;
import com.pengrad.telegrambot.model.ChatMember; import com.pengrad.telegrambot.model.ChatMember;
import com.pengrad.telegrambot.request.GetChatMember; import com.pengrad.telegrambot.request.GetChatMember;
/**
* 对用户进行身份权限验证的管理类
*/
public class MornyTrusted { public class MornyTrusted {
/**
* 群聊id其指向的群聊指示了哪个群的成员是受信任的
* @see #isTrusted(long) 受信检查
*/
public static final long TRUSTED_CHAT_ID = -1001541451710L; public static final long TRUSTED_CHAT_ID = -1001541451710L;
/**
* 用于检查一个 telegram-user 是否受信任<br>
* <br>
* 用户需要受信任才能执行一些对程序甚至是宿主环境而言危险的操作例如关闭程序<br>
* <br>
* 它的逻辑(目前)是检查群聊 {@link #TRUSTED_CHAT_ID} 中这个用户是否为群组管理员
*
* @param userId 需要检查的用户的id
* @return 所传递的用户id对应的用户是否受信任
*/
public static boolean isTrusted (long userId) { public static boolean isTrusted (long userId) {
ChatMember chatMember = MornyCoeur.getAccount().execute(new GetChatMember(TRUSTED_CHAT_ID, userId)).chatMember(); final ChatMember chatMember = MornyCoeur.getAccount().execute(new GetChatMember(TRUSTED_CHAT_ID, userId)).chatMember();
return ( return (
chatMember != null && ( chatMember != null && (
chatMember.status() == ChatMember.Status.administrator || chatMember.status() == ChatMember.Status.administrator ||

View File

@ -2,58 +2,60 @@ package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
@SuppressWarnings("unused") @SuppressWarnings("unused")
public abstract class EventListener { public abstract class EventListener {
public boolean onMessage (Update update) { public boolean onMessage (@Nonnull Update update) {
return false; return false;
} }
public boolean onEditedMessage (Update update) { public boolean onEditedMessage (@Nonnull Update update) {
return false; return false;
} }
public boolean onChannelPost (Update update) { public boolean onChannelPost (@Nonnull Update update) {
return false; return false;
} }
public boolean onEditedChannelPost (Update update) { public boolean onEditedChannelPost (@Nonnull Update update) {
return false; return false;
} }
public boolean onInlineQuery (Update update) { public boolean onInlineQuery (@Nonnull Update update) {
return false; return false;
} }
public boolean onChosenInlineResult (Update update) { public boolean onChosenInlineResult (@Nonnull Update update) {
return false; return false;
} }
public boolean onCallbackQuery (Update update) { public boolean onCallbackQuery (@Nonnull Update update) {
return false; return false;
} }
public boolean onShippingQuery (Update update) { public boolean onShippingQuery (@Nonnull Update update) {
return false; return false;
} }
public boolean onPreCheckoutQuery (Update update) { public boolean onPreCheckoutQuery (@Nonnull Update update) {
return false; return false;
} }
public boolean onPoll (Update update) { public boolean onPoll (@Nonnull Update update) {
return false; return false;
} }
public boolean onPollAnswer (Update update) { public boolean onPollAnswer (@Nonnull Update update) {
return false; return false;
} }
public boolean onMyChatMemberUpdated (Update update) { public boolean onMyChatMemberUpdated (@Nonnull Update update) {
return false; return false;
} }
public boolean onChatMemberUpdated (Update update) { public boolean onChatMemberUpdated (@Nonnull Update update) {
return false; return false;
} }

View File

@ -2,6 +2,7 @@ package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@ -17,7 +18,7 @@ public class EventListenerManager {
private final Function<EventListener, Boolean> exec; private final Function<EventListener, Boolean> exec;
public EventPublisher(Update update, Function<EventListener, Boolean> exec) { public EventPublisher(@Nonnull Update update, @Nonnull Function<EventListener, Boolean> exec) {
this.setName("EVT"+update.updateId()); this.setName("EVT"+update.updateId());
this.exec = exec; this.exec = exec;
} }
@ -36,59 +37,59 @@ public class EventListenerManager {
} }
public static void addListener (EventListener... listeners) { public static void addListener (@Nonnull EventListener... listeners) {
EventListenerManager.listeners.addAll(Arrays.asList(listeners)); EventListenerManager.listeners.addAll(Arrays.asList(listeners));
} }
public static void publishMessageEvent (Update update) { public static void publishMessageEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onMessage(update)).start(); new EventPublisher(update, x -> x.onMessage(update)).start();
} }
public static void publishEditedMessageEvent (Update update) { public static void publishEditedMessageEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onEditedMessage(update)).start(); new EventPublisher(update, x -> x.onEditedMessage(update)).start();
} }
public static void publishChannelPostEvent (Update update) { public static void publishChannelPostEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChannelPost(update)).start(); new EventPublisher(update, x -> x.onChannelPost(update)).start();
} }
public static void publishEditedChannelPostEvent (Update update) { public static void publishEditedChannelPostEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onEditedChannelPost(update)).start(); new EventPublisher(update, x -> x.onEditedChannelPost(update)).start();
} }
public static void publishInlineQueryEvent (Update update) { public static void publishInlineQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onInlineQuery(update)).start(); new EventPublisher(update, x -> x.onInlineQuery(update)).start();
} }
public static void publishChosenInlineResultEvent (Update update) { public static void publishChosenInlineResultEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChosenInlineResult(update)).start(); new EventPublisher(update, x -> x.onChosenInlineResult(update)).start();
} }
public static void publishCallbackQueryEvent (Update update) { public static void publishCallbackQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onCallbackQuery(update)).start(); new EventPublisher(update, x -> x.onCallbackQuery(update)).start();
} }
public static void publishShippingQueryEvent (Update update) { public static void publishShippingQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onShippingQuery(update)).start(); new EventPublisher(update, x -> x.onShippingQuery(update)).start();
} }
public static void publishPreCheckoutQueryEvent (Update update) { public static void publishPreCheckoutQueryEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPreCheckoutQuery(update)).start(); new EventPublisher(update, x -> x.onPreCheckoutQuery(update)).start();
} }
public static void publishPollEvent (Update update) { public static void publishPollEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPoll(update)).start(); new EventPublisher(update, x -> x.onPoll(update)).start();
} }
public static void publishPollAnswerEvent (Update update) { public static void publishPollAnswerEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onPollAnswer(update)).start(); new EventPublisher(update, x -> x.onPollAnswer(update)).start();
} }
public static void publishMyChatMemberUpdatedEvent (Update update) { public static void publishMyChatMemberUpdatedEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onMyChatMemberUpdated(update)).start(); new EventPublisher(update, x -> x.onMyChatMemberUpdated(update)).start();
} }
public static void publishChatMemberUpdatedEvent (Update update) { public static void publishChatMemberUpdatedEvent (@Nonnull Update update) {
new EventPublisher(update, x -> x.onChatMemberUpdated(update)).start(); new EventPublisher(update, x -> x.onChatMemberUpdated(update)).start();
} }

View File

@ -0,0 +1,61 @@
package cc.sukazyo.cono.morny.bot.api;
import cc.sukazyo.cono.morny.util.StringUtils;
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(StringUtils.formatCommand(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);
}
public String getTarget () {
return target;
}
public String getCommand () {
return command;
}
public String[] getArgs () {
return args;
}
public boolean hasArgs () {
return args.length != 0;
}
@Override
public String toString() {
return String.format("{{%s}@{%s}#{%s}}", command, target, Arrays.toString(args));
}
}

View File

@ -3,11 +3,12 @@ package cc.sukazyo.cono.morny.bot.api;
import com.pengrad.telegrambot.UpdatesListener; import com.pengrad.telegrambot.UpdatesListener;
import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.Update;
import javax.annotation.Nonnull;
import java.util.List; import java.util.List;
public class OnUpdate { public class OnUpdate {
public static int onNormalUpdate (List<Update> updates) { public static int onNormalUpdate (@Nonnull List<Update> updates) {
for (Update update : updates) { for (Update update : updates) {
if (update.message() != null) { if (update.message() != null) {
EventListenerManager.publishMessageEvent(update); EventListenerManager.publishMessageEvent(update);

View File

@ -5,10 +5,12 @@ import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager;
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 javax.annotation.Nonnull;
public class OnActivityRecord extends EventListener { public class OnActivityRecord extends EventListener {
@Override @Override
public boolean onMessage (Update update) { public boolean onMessage (@Nonnull Update update) {
if ( if (
update.message().chat().type() == Chat.Type.supergroup || update.message().chat().type() == Chat.Type.supergroup ||
update.message().chat().type() == Chat.Type.group update.message().chat().type() == Chat.Type.group

View File

@ -1,16 +1,25 @@
package cc.sukazyo.cono.morny.bot.event; package cc.sukazyo.cono.morny.bot.event;
import cc.sukazyo.cono.morny.GradleProjectConfigures;
import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornyCoeur;
import cc.sukazyo.cono.morny.MornySystem; import cc.sukazyo.cono.morny.MornySystem;
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.bot.api.InputCommand;
import cc.sukazyo.cono.morny.bot.event.on_commands.GetUsernameAndId; import cc.sukazyo.cono.morny.bot.event.on_commands.GetUsernameAndId;
import cc.sukazyo.cono.morny.util.StringUtils;
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 com.pengrad.telegrambot.request.SendSticker;
import javax.annotation.Nonnull;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import static cc.sukazyo.cono.morny.Logger.logger; import static cc.sukazyo.cono.morny.Logger.logger;
public class OnCommandExecute extends EventListener { public class OnCommandExecute extends EventListener {
@ -21,42 +30,38 @@ public class OnCommandExecute extends EventListener {
private static final String EXIT_403_STICKER_ID = "CAACAgEAAxkBAAMqYYYa_7hpXH6hMOYMX4Nh8AVYd74AAnQnAAJ4_MYFRdmmsQKLDZgiBA"; private static final String EXIT_403_STICKER_ID = "CAACAgEAAxkBAAMqYYYa_7hpXH6hMOYMX4Nh8AVYd74AAnQnAAJ4_MYFRdmmsQKLDZgiBA";
@Override @Override
public boolean onMessage (Update event) { public boolean onMessage (@Nonnull Update event) {
if (event.message().text() == null) { if (event.message().text() == null) {
return false; return false; // 检测到无消息文本忽略掉命令处理
} }
String[] command = StringUtils.formatCommand(event.message().text()); final InputCommand command = new InputCommand(event.message().text());
if (command.length == 0) return false; if (command.getTarget() != null && !MornyCoeur.username.equals(command.getTarget())) {
switch (command[0]) { return true; // 检测到命令并非针对 morny退出整个事件处理链
}
switch (command.getCommand()) {
case "/user": case "/user":
case "/user@" + MornyCoeur.USERNAME: GetUsernameAndId.exec(command.getArgs(), event);
GetUsernameAndId.exec(command, event);
break; break;
case "/o": case "/o":
case "/o@" + MornyCoeur.USERNAME:
onCommandOnExec(event); onCommandOnExec(event);
break; break;
case "/hi": case "/hi":
case "/hi@" + MornyCoeur.USERNAME:
case "/hello": case "/hello":
case "/hello@" + MornyCoeur.USERNAME:
onCommandHelloExec(event); onCommandHelloExec(event);
break; break;
case "/exit": case "/exit":
case "/exit@" + MornyCoeur.USERNAME:
onCommandExitExec(event); onCommandExitExec(event);
break; break;
case "/version": case "/version":
case "/version@" + MornyCoeur.USERNAME:
onCommandVersionExec(event); onCommandVersionExec(event);
break; break;
default: default:
return false; return false; // 无法解析的命令转交事件链后代处理
} }
return true; return true; // 命令执行成功标记事件为已处理退出事件链
} }
private void onCommandOnExec (Update event) { private void onCommandOnExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
ONLINE_STATUS_RETURN_STICKER_ID ONLINE_STATUS_RETURN_STICKER_ID
@ -64,7 +69,7 @@ public class OnCommandExecute extends EventListener {
); );
} }
private void onCommandHelloExec (Update event) { private void onCommandHelloExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
HELLO_STICKER_ID HELLO_STICKER_ID
@ -72,7 +77,7 @@ public class OnCommandExecute extends EventListener {
); );
} }
private void onCommandExitExec (Update event) { private void onCommandExitExec (@Nonnull Update event) {
if (MornyTrusted.isTrusted(event.message().from().id())) { if (MornyTrusted.isTrusted(event.message().from().id())) {
MornyCoeur.getAccount().execute(new SendSticker( MornyCoeur.getAccount().execute(new SendSticker(
event.message().chat().id(), event.message().chat().id(),
@ -91,16 +96,23 @@ public class OnCommandExecute extends EventListener {
} }
} }
private void onCommandVersionExec (Update event) { private void onCommandVersionExec (@Nonnull Update event) {
MornyCoeur.getAccount().execute(new SendMessage( MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(), event.message().chat().id(),
String.format( String.format("""
"version:\n" + version:
"\t<code>%s</code>\n" + <code> </code><code>%s</code>
"core md5_hash:\n" + core md5_hash:
"\t<code>%s</code>", <code> </code><code>%s</code>
compile timestamp:
<code> </code><code>%d</code>
<code> </code><code>%s [UTC]</code>""",
MornySystem.VERSION, MornySystem.VERSION,
MornySystem.getJarMd5() MornySystem.getJarMd5(),
GradleProjectConfigures.COMPILE_TIMESTAMP,
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss:SSS").format(LocalDateTime.ofInstant(
Instant.ofEpochMilli(GradleProjectConfigures.COMPILE_TIMESTAMP),
ZoneId.ofOffset("UTC", ZoneOffset.UTC)))
) )
).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML)); ).replyToMessageId(event.message().messageId()).parseMode(ParseMode.HTML));
} }

View File

@ -8,11 +8,13 @@ import com.pengrad.telegrambot.model.User;
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 javax.annotation.Nonnull;
public class OnUserSlashAction extends EventListener { public class OnUserSlashAction extends EventListener {
@Override @Override
public boolean onMessage (Update event) { public boolean onMessage (@Nonnull Update event) {
String text = event.message().text(); final String text = event.message().text();
if (text == null) return false; if (text == null) return false;
if (text.startsWith("/")) { if (text.startsWith("/")) {
@ -31,12 +33,12 @@ public class OnUserSlashAction extends EventListener {
prefixLength = 2; prefixLength = 2;
} }
String[] action = StringUtils.formatCommand(text.substring(prefixLength)); final String[] action = StringUtils.formatCommand(text.substring(prefixLength));
String verb = action[0]; final String verb = action[0];
boolean hasObject = action.length != 1; final boolean hasObject = action.length != 1;
String object = StringUtils.connectStringArray(action, " ", 1, action.length-1); final String object = StringUtils.connectStringArray(action, " ", 1, action.length-1);
User origin = event.message().from(); final User origin = event.message().from();
User target = (event.message().replyToMessage() == null ? ( final User target = (event.message().replyToMessage() == null ? (
origin origin
): ( ): (
event.message().replyToMessage().from() event.message().replyToMessage().from()

View File

@ -8,11 +8,13 @@ import com.pengrad.telegrambot.request.GetChatMember;
import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendMessage;
import com.pengrad.telegrambot.response.GetChatMemberResponse; import com.pengrad.telegrambot.response.GetChatMemberResponse;
import javax.annotation.Nonnull;
public class GetUsernameAndId { public class GetUsernameAndId {
public static void exec (String[] command, Update event) { public static void exec (@Nonnull String[] args, @Nonnull Update event) {
if (command.length > 2) { MornyCoeur.getAccount().execute(new SendMessage( if (args.length > 1) { MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(), event.message().chat().id(),
"[Unavailable] Too much arguments." "[Unavailable] Too much arguments."
).replyToMessageId(event.message().messageId())); return; } ).replyToMessageId(event.message().messageId())); return; }
@ -22,9 +24,9 @@ public class GetUsernameAndId {
if ( event.message().replyToMessage()!= null) { if ( event.message().replyToMessage()!= null) {
userId = event.message().replyToMessage().from().id(); userId = event.message().replyToMessage().from().id();
} }
if (command.length > 1) { if (args.length > 0) {
try { try {
userId = Long.parseLong(command[1]); userId = Long.parseLong(args[0]);
} catch (NumberFormatException e) { } catch (NumberFormatException e) {
MornyCoeur.getAccount().execute(new SendMessage( MornyCoeur.getAccount().execute(new SendMessage(
event.message().chat().id(), event.message().chat().id(),
@ -42,7 +44,7 @@ public class GetUsernameAndId {
return; return;
} }
GetChatMemberResponse response = MornyCoeur.getAccount().execute( final GetChatMemberResponse response = MornyCoeur.getAccount().execute(
new GetChatMember(event.message().chat().id(), userId) new GetChatMember(event.message().chat().id(), userId)
); );
@ -54,22 +56,25 @@ public class GetUsernameAndId {
return; return;
} }
User user = response.chatMember().user(); final User user = response.chatMember().user();
StringBuilder userInformation = new StringBuilder(); final StringBuilder userInformation = new StringBuilder();
userInformation.append(String.format( userInformation.append(String.format(
"userid :\n" + """
"- <code>%d</code>\n" + userid :
"username :\n" + - <code>%d</code>
"- <code>%s</code>", username :
userId, user.username() - <code>%s</code>""",
userId, user.username()
)); ));
if (user.firstName() == null) { if (user.firstName() == null) {
userInformation.append("\nfirstname : <u>null</u>"); userInformation.append("\nfirstname : <u>null</u>");
} else { } else {
userInformation.append(String.format( userInformation.append(String.format(
"\nfirstname :\n" + """
"- <code>%s</code>",
firstname :
- <code>%s</code>""",
user.firstName() user.firstName()
)); ));
} }
@ -77,15 +82,19 @@ public class GetUsernameAndId {
userInformation.append("\nlastname : <u>null</u>"); userInformation.append("\nlastname : <u>null</u>");
} else { } else {
userInformation.append(String.format( userInformation.append(String.format(
"\nlastname :\n" + """
"- <code>%s</code>",
lastname :
- <code>%s</code>""",
user.lastName() user.lastName()
)); ));
} }
if (user.languageCode() != null) { if (user.languageCode() != null) {
userInformation.append(String.format( userInformation.append(String.format(
"\nlanguage-code :\n" + """
"- <code>%s</code>",
language-code :
- <code>%s</code>""",
user.languageCode() user.languageCode()
)); ));
} }

View File

@ -1,6 +1,6 @@
package cc.sukazyo.cono.morny.util; package cc.sukazyo.cono.morny.util;
import java.io.File; import javax.annotation.Nonnull;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.math.BigInteger; import java.math.BigInteger;
@ -9,19 +9,19 @@ import java.security.NoSuchAlgorithmException;
public class FileUtils { public class FileUtils {
public static String getMD5Three (String path) { @Nonnull
BigInteger bi; public static String getMD5Three (@Nonnull String path) {
final BigInteger bi;
try { try {
byte[] buffer = new byte[8192]; final byte[] buffer = new byte[8192];
int len; int len;
MessageDigest md = MessageDigest.getInstance("MD5"); final MessageDigest md = MessageDigest.getInstance("MD5");
File f = new File(path); final FileInputStream fis = new FileInputStream(path);
FileInputStream fis = new FileInputStream(f);
while ((len = fis.read(buffer)) != -1) { while ((len = fis.read(buffer)) != -1) {
md.update(buffer, 0, len); md.update(buffer, 0, len);
} }
fis.close(); fis.close();
byte[] b = md.digest(); final byte[] b = md.digest();
bi = new BigInteger(1, b); bi = new BigInteger(1, b);
} catch (NoSuchAlgorithmException | IOException e) { } catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace(System.out); e.printStackTrace(System.out);

View File

@ -1,22 +1,26 @@
package cc.sukazyo.cono.morny.util; package cc.sukazyo.cono.morny.util;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
public class StringUtils { public class StringUtils {
@Nonnull
public static String repeatChar (char c, int i) { public static String repeatChar (char c, int i) {
char[] chars = new char[i]; final char[] chars = new char[i];
Arrays.fill(chars, c); Arrays.fill(chars, c);
return new String(chars); return new String(chars);
} }
public static String[] formatCommand (String com) { @Nonnull
public static String[] formatCommand (@Nonnull String com) {
ArrayList<String> arr = new ArrayList<>(); final ArrayList<String> arr = new ArrayList<>();
StringBuilder tmp = new StringBuilder(); final StringBuilder tmp = new StringBuilder();
char[] coma = com.toCharArray(); final char[] coma = com.toCharArray();
for (int i = 0; i < coma.length; i++) { for (int i = 0; i < coma.length; i++) {
if (coma[i] == ' ') { if (coma[i] == ' ') {
if (!tmp.toString().equals("")) { arr.add(tmp.toString()); } if (!tmp.toString().equals("")) { arr.add(tmp.toString()); }
@ -43,14 +47,17 @@ public class StringUtils {
if (!tmp.toString().equals("")) { arr.add(tmp.toString()); } if (!tmp.toString().equals("")) { arr.add(tmp.toString()); }
tmp.setLength(0); tmp.setLength(0);
String[] out = new String[arr.size()]; final String[] out = new String[arr.size()];
arr.toArray(out); arr.toArray(out);
return out; return out;
} }
public static String connectStringArray (String[] array, String connector, int startIndex, int stopIndex) { @Nonnull
StringBuilder builder = new StringBuilder(); public static String connectStringArray (
@Nonnull String[] array, @Nonnull String connector, @Nonnegative int startIndex, @Nonnegative int stopIndex
) {
final StringBuilder builder = new StringBuilder();
for (int i = startIndex; i < stopIndex; i++) { for (int i = startIndex; i < stopIndex; i++) {
builder.append(array[i]); builder.append(array[i]);
builder.append(connector); builder.append(connector);