diff --git a/build.gradle b/build.gradle index 0bd3331..2d6ec16 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,13 @@ plugins { id 'java' + id 'java-library' id 'maven-publish' id 'application' id 'com.github.johnrengelman.shadow' version '7.1.0' } group 'cc.sukazyo' -version '0.3.3' +version '0.3.4.4' project.ext.archiveBaseName = 'Coeur_Morny_Cono' project.ext.artifactId = 'morny-coeur' mainClassName = 'cc.sukazyo.cono.morny.MornyCoeur' @@ -17,25 +18,37 @@ repositories { 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' - testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.1' + implementation 'com.github.pengrad:java-telegram-bot-api:5.4.0' + + 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) { - 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 { useJUnitPlatform() } java { + + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + withSourcesJar() + } tasks.withType(JavaCompile) { diff --git a/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java new file mode 100644 index 0000000..87c1e24 --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/GradleProjectConfigures.java @@ -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; +} diff --git a/src/main/java/cc/sukazyo/cono/morny/Logger.java b/src/main/java/cc/sukazyo/cono/morny/Logger.java index 9727f55..f48b6d1 100644 --- a/src/main/java/cc/sukazyo/cono/morny/Logger.java +++ b/src/main/java/cc/sukazyo/cono/morny/Logger.java @@ -2,30 +2,78 @@ package cc.sukazyo.cono.morny; import cc.sukazyo.cono.morny.util.StringUtils; +import javax.annotation.Nonnull; + +/** + * Morny 的简单控制台 log 记录器 + */ public class Logger { + /** Morny 的控制台 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")); } - public void warn (String message) { - waring(message); + /** + * [WARN] 级别的 log 消息
+ * {@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")); } - 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")); } - private String formatMessage (String message, String level) { - String prompt = String.format("[%s][%s]", System.currentTimeMillis(), Thread.currentThread().getName()); - String levelStr = String.format("[%s]", level); - String newline = "\n" + StringUtils.repeatChar('\'', prompt.length()) + levelStr; + /** + * 将传入的消息和消息元数据重新整理为固定格式
+ *
+ * 这个方法会{@link System#currentTimeMillis() 获取当前时间}和{@link Thread#currentThread() 当前线程}的名称, + * 然后将数据整理为 {@code [][][]} 格式。
+ * 如果消息是多行的,则每行的开头都会被加入 {@code [][][]} 这样的前缀, + * 不过,前缀中 {@code [][]} 会被转换为等长的 {@code '} 字串。 + *
+ * 最终的 format 格式将会类似于以下的模样:

+[1019284827][EVT388223][INFO]Something message got:
+'''''''''''''''''''''''[INFO] - data source: 19773
+'''''''''''''''''''''''[INFO] - message raw: noh2q0jwd9j-jn-9jq92-ed
+	 * 
+ * + * @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); } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index f652188..ed1556a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -6,14 +6,67 @@ import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; import com.pengrad.telegrambot.TelegramBot; 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; +/** + * Morny Cono 核心
+ *
+ * - 的程序化入口类,保管着 morny 的核心属性
+ */ public class MornyCoeur { + /** morny 的 bot 账户 */ private static TelegramBot account; - public static final String USERNAME = "morny_cono_annie_bot"; + /** + * morny 的 bot 账户的用户名
+ *
+ * 出于技术限制,这个字段目前是写死的 + */ + public static String username; - public static void main (String[] args) { + /** + * 程序入口
+ *
+ * 会从命令行参数取得初始化数据并初始化程序和bot
+ *
+ * - 第一个参数({@code args[0]})必序传递,值为 telegram-bot 的 api-token
+ * - 第二个参数可选 {@code --no-hello} 和 {@code --only-hello}, + * 前者表示不输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语}, + * 后者表示只输出{@link MornyHello#MORNY_PREVIEW_IMAGE_ASCII 欢迎标语}而不运行程序逻辑
+ *
+ * 或者,在第一个参数处使用 {@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]))) logger.info(MornyHello.MORNY_PREVIEW_IMAGE_ASCII); @@ -24,6 +77,10 @@ public class MornyCoeur { configureSafeExit(); 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]); } 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 () { TrackerDataManager.DAEMON.interrupt(); TrackerDataManager.trackingLock.lock(); } + /** + * 为程序在虚拟机上添加退出钩子 + */ private static void configureSafeExit () { Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup)); } - public static TelegramBot login (String key) { - TelegramBot account = new TelegramBot(key); + /** + * 登录 bot
+ *
+ * 会反复尝试三次进行登录。如果登录失败,则会直接抛出 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..."); for (int i = 1; i < 4; i++) { if (i != 1) logger.info("retrying..."); try { - String username = account.execute(new GetMe()).user().username(); - if (!USERNAME.equals(username)) - throw new RuntimeException("Required the bot @"+USERNAME + " but @"+username + " logged in!"); + final String username = account.execute(new GetMe()).user().username(); + if (username != null && !MornyCoeur.username.equals(username)) + throw new RuntimeException("Required the bot @" + MornyCoeur.username + " but @" + username + " logged in!"); + else MornyCoeur.username = username; logger.info("Succeed login to @" + username); return account; } catch (Exception e) { @@ -66,6 +141,11 @@ public class MornyCoeur { throw new RuntimeException("Login failed.."); } + /** + * 获取登录成功后的 telegram bot 对象 + * + * @return {@link #account MornyCoeur.account} + */ public static TelegramBot getAccount () { return account; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyHello.java b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java index 4004403..8a3725b 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyHello.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java @@ -1,8 +1,70 @@ package cc.sukazyo.cono.morny; +/** + * {@link #MORNY_PREVIEW_IMAGE_ASCII} 静态数据存放类 + */ @SuppressWarnings("all") public class MornyHello { - public static final String MORNY_PREVIEW_IMAGE_ASCII = "ttt///t/////fucj(\\tvnxtf{< .' .. .:i` . . ^!`l|-^i+,!_[:1/|{i?//\\//jf\\\\\\///\\\\\\\\//\\\\\\//////\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\/\\\\//\\\\\\///\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\fnncvvU0O00QCx!!\". .. ` \n" + "tt//////////\\jzjrucnjt/?{j,,\"' . .' .. .\":. .;{: ' \"`.,1(<.\"i?)\\(-}\\\\\\(((\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\//////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\///\\\\///\\\\\\\\\\\\\\\\|\\\\\\\\\\\\|\\\\\\\\\\\\\\\\tvXvuXcxn/[Il)({_:.. .\"` ., \n" + "//////////////////\\////|)/([}-_<+[]>.^^\"\"[<'`^` .''\"\"`'.`'`\"i! ^!>l:' :<\" !!.IiI`+l^^`i>_<`??)1;^{\\\\\\\\\\{|({({|/\\]I)\\\\()\\(]}|\\\\||\\|||\\/\\\\\\\\\\\\|||\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\||\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\\\////\\\\\\\\\\\\\\\\\\//|{{?{|)[[-;\n" + "ttt/tt//////////////////{)(\\t(/tt/1~I}{-1\\_^])1_+[{|(?\"<1~>>+!+[}11)}[(1}]};^1\\|~_1}{I:-1(I+)(|))|\\\\/////////\\\\////\\\\\\/////\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\//\\\\///\\//||\\////|)(//\\\\///){\\/\\(11|///({)//({[1\\\\\\\\\\\\\\\\\\\\|\\/\\\\\\/\\//////////\\\\\\\\\\\\\\\\\\//\\\\\\\\\\///////\\|\\\\\\\\//////\\\\///\\\n" + "tttt/////////////\\///////\\||///////t//|(|)|}|\\/(\\\\(//(l_{{. ... \">+<^'I!: ^<(\\\\1}1//\\\\\\//////////\\\\///\\/\\///\\\\\\\\\\\\//\\\\//\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\(/\\{\n" + "t////////////////////////////////////////////////////////\\/\\\\///////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\||\\|\\\\\\\\\\|\\\\\\\\/\\\\|\\\\\\\\\\\\////((|///}!:,\":,^`. .;' ' '^..':. ^!;. .^^ '^^`. '' ...I[{!>:^;_i:'~\\ttt/////tt//\\\\////////////\\\\\\\\/\\\\\\\\\\\\/\\\\\\\\/\\\\\\\\\\)}-+[+I??i\n" + "ttt////////////////////////////////////////////////////////\\\\//\\//\\\\/\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\||\\\\\\/\\//\\\\\\\\\\\\\\\\\\/\\|\\\\\\////\\1;``^;<>+!\">__+I `' .. \"'. .;\" ;;. .:^ ``,,;'` .;]I ,-_-|\\////t////t///////\\/\\\\\\\\//\\\\\\\\\\\\//\\\\\\///////-II1ttt///tttt/////////\\/\\/////\\\\\\\\////t|+<}?!-]l<{[[1-+]\n" + "t//////////////////////////////////////\\/////////\\////////////////\\//////\\//tttttttttt//////////////////////////)_)t)|}1f/{<.^,^:~: . .. '''^:-|/> '-/}-_?\\/)-{?(//\\(\\tt////\\///\\\\\\\\\\\\\\///t1.;); .l~` '\"\n" + "///////////////////////////////////////////tt/t(|tt//]+{t\\{][|////\\//////////ttttt///t//t/////////////\\//////|//{[|f}!l>~++~<<\\//]l~?])tt//\\\\\\\\\\/\\\\///\\\\|?<_}[\"^!;I^;]:. .\n" + "////////////////////////////////tttt/|{[1)]~!!+>!<_(/|[-<\"i!l,]tt//ttt/t////ttt//t///ttttttt////////t//t//ttt){+. :?^ '. l_-!+l;;;|!!>~~il!lllllllllll!!lI:`'. .' :I;]_}>,?tf:.+fft)l+1//\\~`'I-(//\\/t/|/(-1[)/?>>II:' '.`';-'` \n" + "/////////////////////////t//()\\1_<>il^'''' ,!>;.,.'{tti `~tf(`'-(|fffftttttttttt/tttttttttttt///tttft//(t|]?-+!^ .\"`. `. ;!I,. .?{il-\\_!~<>>!lII;IllIIIIIllllllllllI;;:,:,' '\"^`(f{+{>' .<{t(I!}/||t> ^(//}>;:1\\]: \"[:\"` ^<: . II.'.. \n" + "///////////////ttt//tt((-!+}\"'^. I, ,?<:' ,:;!>~',!_~{}-1]`^!}_+\\ttttttt/tttttttfff/tt\\(||]-?+;,:\"l\" '..'.. ?]l:\" -(lI;,~?~!IIIlllI:IIlI;IIIIlllllllIIIIIllII!; . . '^^;~), \"~!}\\/t//\\\\/_. '\". '_i !i''' \n" + "tt//t///ttt///(]<>l>][l\"'.`,. ^.^. ii ;; ~>>>. .i~I'^^<}), .;|tfftttttttttttf\\]}t-!,,I` .^ '. !: . .\",I;. ^,I<)/-l:;llllllI;lIll;;IIIIlllIllIlIIlIllI;><. ' .;}\". '.:+](ft\\}(t/t{;<\\{l^>}!^l\\/{>1/t(lI:I!+<<\". ':\" \n" + "t//tttt|?+!I!:' '` .`. ...... `^ \"<^.;`^\"'`,!\".,^^^.,?)!. [f/+>(/tttft\\tff|+^,!' '^: >[,++:`' .I^ . _?!:^. ;~{/?//)! __::. ':. '. \n" + "t/\\}[{]\",il'`!-<-]:`'^` .. '' .^+:'. .^'\"i:`^. ';`:<_|>'.?/t/!\"<)ffftf)]]!'II.,l ^' ''. '\";\" .' `Il, ;]>]j_;lI;ll;!!llIII>~IIIIII;: ;,~.',.<:`, 'I_|\\; .i|/]^ ?(}\\/////\\i' '' ....'^ \n" + "tf1<}i `^. `I` .I?\"'. . . ^' .^' .'` .\". >}_.I|t{_(tf({~,~(); ')t};.><,. .. . .. . . .]}^{j1IlllIlI!1IlllII?{IIIIIlI;[1!I;IIllIIlIIllI<]_;+/t\\(|\\/1,' \"` ... \n" + ")+::((:^' ll .,` . . ..'. ' :+'`{tj{,l: ^;\"..;!\"^.I?' '~; .` .'. . `1+ [x?-:lIlll!]r-IllI~~{~I>lllll[i\\--+;;I~IIIII!l;x] \"I\"-<<_> >i.' l{}:itf/}[/\\)(\\}))|(:^^..'. `\". \n" + ">.._f|i.:l,;^^''__. .^' `' \"+,`]1i`!1_. ^l: .\". .` '1I +JIt!IIlll;]\\) >1}c(_i(!IllI_l;(f. ,_\";~~+^ .. .;-i '+([i+: !//1](||/\\(?:^..^^ \n" + "i'\"}_,.` ''^... '. `. ^<`_> .. +x??_~]:[|!,.ll` . {+ ;Y[^|,>~IlIIf\\ {/;I!\\ [[-'<+l-{ _??]f\\n]lllI[!;1v` `+\"]-}]~\" ..'`l, ''i-` l+?\\\\\\/t{!)t[:' .^^ \n" + ";,:: :,^..;:. . i+..;^ `_ ]]<-?l``-]' I>]?+. ^-|\\\\_I?]{t/?` .... \n" + "^(\\]I^~?;.\"!\" . .. . ^<^ :( >t) _[il>|+:(U<1nYQ0Xx\\> . .~xcXXYzx(n?IllI}\">xCI .:1]_-\" . .^. `}>!}((1-^,+?\" .. \n" + "1+,~I.(l' . ... ~|r:;`.+I?\\};+t) \"\".-?;lI>(;]xn. '_>]!+. '^'`l:11l[|((+?: . \n" + "[-`.':;..\"\"' lv|. .:_(;I!u> ^,\",^. .;?I]?IlI}n[^ ')(I' `tlII>x1\" <}1{)l \"~+ |[II;\\[:~zl .i;. .... `\"i\\\\}]..!' ^.... \n" + "-\" ` ' . \":` .|+<<;!\\U)>^ '^`' ^\"I?)c-;j/ <1I~;` .!}\\(: .;\"`' \n" + "' 'l, .' ~[><+;!f()nn|]!:' ..^:!+1fcjx}}v!_)})>|n` ~^ ^;\"'` .<+I<)/||\\i'\"<\"'^ `. \n" + " II ^}_'+_!fI?_/-jJjUr\\\\ucJJz\\|J>}?-j{]^ni\" .;](),.;-<`' .^ .^' \n" + " +[\" +]{.`i;I; ::.\"!??l.^Ywj}<, (n, ,~_:` .,, ` `` ' '... ^+-l,]}]}\\j/!. . ` 'I<~`'{tl..^` \n" + " '' .,<{[>\" i/i\" `-[; ,<_[>^i_l,:^_! ',+l.. ^,:,,. ;~>l;^ l> ',;I^???~,'l\".. .. \n" + " ;{?l. !+ .. .<1i '^' \"}|{:-+-;?\\[)-] ^:l1-:. '' '`, . ';.`~^ '. ..^`. \n" + " 'i+;]}!,. <))\\!<|ji >((_}}?t)}\\\\v|]?jI!), \"lf!l. ... .^ . \". \n" + " .+{>` l/z\\!,>\"\"I+~_){]vQjut_~~>>>_-<]<-)f\":l_v){\\/1}}}{t/\\0?z~. ^' `-l . . .. . \n" + " l\\ :_>>i:^+\\)_-]!:>-+l'...`^;1!^ 'l>})l\\n\\Qt?]?]})1{][[(XC>^ ...\n" + " ^-+^.i-((?!\"`:>l<[~<]nQY+?????][{\\cmO||l . ''..\n" + " '|: '^[{~)\\_+++))1{uxnvt(t){{[[u0\\1|({1()){-?|xfc: .. \n" + " .<-, ]-]]]})11)){{{{}{{)|{}}{1{111{11{{}}]_!\"x\\]Xf \n" + " ,]<\\}][[[[[[[[[[[[[[[]][[[[[[[[][[[[[[]?-+!YC{z} .`\n" + " \"_[}?]][[[[[[[[[[[[[[[[[[][[[[[[[[[[[[[[[]vn\\?. ^\n" + " ^. ;{_(_??][[[[[[[[[[[[[[[[[|[[[[[[[[[[[[[[[[v_(]^ \n" + " .' '. :t>/?[[[[[[[[[[[[[[[[[[[]t\\][[[[[[[[[[][]?u;()_ .. .'. . .\n" + " `. .` :)!j_]][[[]]]]][[[[[[[[[[}j(/{[[[[[][[}1{~n!)ft . .. .. . .. .\n" + " \". ..' .` . \"' .^\":;~ti{\\1]][]]]??]]]]][]]??[){[}[[[[?+}]!^':``^``,`;I.!<>?>:??i:;-;,<_..^,Ii: 'l,i+```' ..'!; ''. ?~.'<+ .li!:,1?}[[[[[[[[[[[[[[[[[[[[)1}]t[:, \"O\" . .' . ^!' . \". i+' . \n" + "^. . .':`'`~/1,-<-~^'^'^^,`..\"i_>^. `1t]!,^I]l^;`,I::_?]?[!:;`.`\"'`l!l<1f{~>;]\\1(]I>~l!l[<,,;`lI,~},^>!>l'...'\": 'x' ]l>i .:I1[~]]]]}}}}}[[}}[}}}}}[[]]]??1}}[}~;:>vx. :;..:??,.' ^` I;>.\";:\"^' .. ^'.^\"\" ' .!},' .\n" + "~!;:!\".\":i\"^_/|]^li(\\1;;it{' .[\\fft+<}(/{}/)|f||'.^{/[)?!:(?+-,I+fjtil\"'\"+fj{i:',!!;!!^:.`r. r; !l'\"i1?!i>~+_?][[[?-???]][[]?-+-]??+~<_{[_l?> ''^l;l`-}<`^.i>+l ``;I\":+?!~-l ,>>l.'.;. ':!!(/!\":,I\n" + "/t1ffft+{jff/ttff)];)?1(/tt\\/t/tfttttfftf/1\\|t\\|/?<_]_]{<_]/f({fffjttf/[i>1//|tft|\" :<~:+}, ]>if\" .:-~ >) ^`^l)f(_{/\\}-+1\\()t-{j/]!:^'l<]\\)+ .\"_?I_{\n" + "ffft)|)(t[_-{tjjrjrj/{(||}(rjj\\1)I<\\((ffj/rjffttjffftrjfffrtfff/f[1jjffffftt//)}tttff/ttt[<{rj}tf1?<:~{/j)>)fttf|?)tfffftt1_;+tf1-1|~i1, >;:} '1_ ;( .. .. `:\"_1{}tjtvj)vjr/|jfff/<(tf)+1/)1j)~~-[j[l|[(/\\j{:-]]([}\\t"; + /** + * 系统的开屏欢迎语 ASCII 字符画字段 + */ + public static final String MORNY_PREVIEW_IMAGE_ASCII = """ + ttt///t/////fucj(\\tvnxtf{< .' .. .:i` . . ^!`l|-^i+,!_[:1/|{i?//\\//jf\\\\\\///\\\\\\\\//\\\\\\//////\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\/\\\\//\\\\\\///\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\fnncvvU0O00QCx!!". .. ` \s + tt//////////\\jzjrucnjt/?{j,,"' . .' .. .":. .;{: ' "`.,1(<."i?)\\(-}\\\\\\(((\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\//////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\|\\\\\\\\\\\\///\\\\///\\\\\\\\\\\\\\\\|\\\\\\\\\\\\|\\\\\\\\\\\\\\\\tvXvuXcxn/[Il)({_:.. ."` .,\s + //////////////////\\////|)/([}-_<+[]>.^^""[<'`^` .''""`'.`'`"i! ^!>l:' :<" !!.IiI`+l^^`i>_<`??)1;^{\\\\\\\\\\{|({({|/\\]I)\\\\()\\(]}|\\\\||\\|||\\/\\\\\\\\\\\\|||\\\\\\\\\\\\\\\\//\\\\\\\\/\\\\\\\\||\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\\\\\\\\\\\\\////\\\\\\\\\\\\\\\\\\//|{{?{|)[[-; + ttt/tt//////////////////{)(\\t(/tt/1~I}{-1\\_^])1_+[{|(?"<1~>>+!+[}11)}[(1}]};^1\\|~_1}{I:-1(I+)(|))|\\\\/////////\\\\////\\\\\\/////\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\//\\\\///\\//||\\////|)(//\\\\///){\\/\\(11|///({)//({[1\\\\\\\\\\\\\\\\\\\\|\\/\\\\\\/\\//////////\\\\\\\\\\\\\\\\\\//\\\\\\\\\\///////\\|\\\\\\\\//////\\\\///\\ + tttt/////////////\\///////\\||///////t//|(|)|}|\\/(\\\\(//(l_{{. ... ">+<^'I!: ^<(\\\\1}1//\\\\\\//////////\\\\///\\/\\///\\\\\\\\\\\\//\\\\//\\\\\\\\\\\\\\\\\\\\\\\\\\\\///\\(/\\{ + t////////////////////////////////////////////////////////\\/\\\\///////\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\/\\\\\\\\\\\\||\\|\\\\\\\\\\|\\\\\\\\/\\\\|\\\\\\\\\\\\////((|///}!:,":,^`. .;' ' '^..':. ^!;. .^^ '^^`. '' ...I[{!>:^;_i:'~\\ttt/////tt//\\\\////////////\\\\\\\\/\\\\\\\\\\\\/\\\\\\\\/\\\\\\\\\\)}-+[+I??i + ttt////////////////////////////////////////////////////////\\\\//\\//\\\\/\\\\\\\\\\\\\\\\\\\\//\\\\\\\\\\\\||\\\\\\/\\//\\\\\\\\\\\\\\\\\\/\\|\\\\\\////\\1;``^;<>+!">__+I `' .. "'. .;" ;;. .:^ ``,,;'` .;]I ,-_-|\\////t////t///////\\/\\\\\\\\//\\\\\\\\\\\\//\\\\\\///////-II1ttt///tttt/////////\\/\\/////\\\\\\\\////t|+<}?!-]l<{[[1-+] + t//////////////////////////////////////\\/////////\\////////////////\\//////\\//tttttttttt//////////////////////////)_)t)|}1f/{<.^,^:~: . .. '''^:-|/> '-/}-_?\\/)-{?(//\\(\\tt////\\///\\\\\\\\\\\\\\///t1.;); .l~` '" + ///////////////////////////////////////////tt/t(|tt//]+{t\\{][|////\\//////////ttttt///t//t/////////////\\//////|//{[|f}!l>~++~<<\\//]l~?])tt//\\\\\\\\\\/\\\\///\\\\|?<_}["^!;I^;]:. . + ////////////////////////////////tttt/|{[1)]~!!+>!<_(/|[-<"i!l,]tt//ttt/t////ttt//t///ttttttt////////t//t//ttt){+. :?^ '. l_-!+l;;;|!!>~~il!lllllllllll!!lI:`'. .' :I;]_}>,?tf:.+fft)l+1//\\~`'I-(//\\/t/|/(-1[)/?>>II:' '.`';-'` \s + /////////////////////////t//()\\1_<>il^'''' ,!>;.,.'{tti `~tf(`'-(|fffftttttttttt/tttttttttttt///tttft//(t|]?-+!^ ."`. `. ;!I,. .?{il-\\_!~<>>!lII;IllIIIIIllllllllllI;;:,:,' '"^`(f{+{>' .<{t(I!}/||t> ^(//}>;:1\\]: "[:"` ^<: . II.'.. \s + ///////////////ttt//tt((-!+}"'^. I, ,?<:' ,:;!>~',!_~{}-1]`^!}_+\\ttttttt/tttttttfff/tt\\(||]-?+;,:"l" '..'.. ?]l:" -(lI;,~?~!IIIlllI:IIlI;IIIIlllllllIIIIIllII!; . . '^^;~), "~!}\\/t//\\\\/_. '". '_i !i''' \s + tt//t///ttt///(]<>l>][l"'.`,. ^.^. ii ;; ~>>>. .i~I'^^<}), .;|tfftttttttttttf\\]}t-!,,I` .^ '. !: . .",I;. ^,I<)/-l:;llllllI;lIll;;IIIIlllIllIlIIlIllI;><. ' .;}". '.:+](ft\\}(t/t{;<\\{l^>}!^l\\/{>1/t(lI:I!+<<". ':" \s + t//tttt|?+!I!:' '` .`. ...... `^ "<^.;`^"'`,!".,^^^.,?)!. [f/+>(/tttft\\tff|+^,!' '^: >[,++:`' .I^ . _?!:^. ;~{/?//)! __::. ':. '. \s + t/\\}[{]",il'`!-<-]:`'^` .. '' .^+:'. .^'"i:`^. ';`:<_|>'.?/t/!"<)ffftf)]]!'II.,l ^' ''. '";" .' `Il, ;]>]j_;lI;ll;!!llIII>~IIIIII;: ;,~.',.<:`, 'I_|\\; .i|/]^ ?(}\\/////\\i' '' ....'^ \s + tf1<}i `^. `I` .I?"'. . . ^' .^' .'` .". >}_.I|t{_(tf({~,~(); ')t};.><,. .. . .. . . .]}^{j1IlllIlI!1IlllII?{IIIIIlI;[1!I;IIllIIlIIllI<]_;+/t\\(|\\/1,' "` ... \s + )+::((:^' ll .,` . . ..'. ' :+'`{tj{,l: ^;"..;!"^.I?' '~; .` .'. . `1+ [x?-:lIlll!]r-IllI~~{~I>lllll[i\\--+;;I~IIIII!l;x] "I"-<<_> >i.' l{}:itf/}[/\\)(\\}))|(:^^..'. `". \s + >.._f|i.:l,;^^''__. .^' `' "+,`]1i`!1_. ^l: .". .` '1I +JIt!IIlll;]\\) >1}c(_i(!IllI_l;(f. ,_";~~+^ .. .;-i '+([i+: !//1](||/\\(?:^..^^ \s + i'"}_,.` ''^... '. `. ^<`_> .. +x??_~]:[|!,.ll` . {+ ;Y[^|,>~IlIIf\\ {/;I!\\ [[-'<+l-{ _??]f\\n]lllI[!;1v` `+"]-}]~" ..'`l, ''i-` l+?\\\\\\/t{!)t[:' .^^ \s + ;,:: :,^..;:. . i+..;^ `_ ]]<-?l``-]' I>]?+. ^-|\\\\_I?]{t/?` .... \s + ^(\\]I^~?;."!" . .. . ^<^ :( >t) _[il>|+:(U<1nYQ0Xx\\> . .~xcXXYzx(n?IllI}">xCI .:1]_-" . .^. `}>!}((1-^,+?" .. \s + 1+,~I.(l' . ... ~|r:;`.+I?\\};+t) "".-?;lI>(;]xn. '_>]!+. '^'`l:11l[|((+?: . \s + [-`.':;..""' lv|. .:_(;I!u> ^,",^. .;?I]?IlI}n[^ ')(I' `tlII>x1" <}1{)l "~+ |[II;\\[:~zl .i;. .... `"i\\\\}]..!' ^.... \s + -" ` ' . ":` .|+<<;!\\U)>^ '^`' ^"I?)c-;j/ <1I~;` .!}\\(: .;"`' \s + ' 'l, .' ~[><+;!f()nn|]!:' ..^:!+1fcjx}}v!_)})>|n` ~^ ^;"'` .<+I<)/||\\i'"<"'^ `. \s + II ^}_'+_!fI?_/-jJjUr\\\\ucJJz\\|J>}?-j{]^ni" .;](),.;-<`' .^ .^' \s + +[" +]{.`i;I; ::."!??l.^Ywj}<, (n, ,~_:` .,, ` `` ' '... ^+-l,]}]}\\j/!. . ` 'I<~`'{tl..^` \s + '' .,<{[>" i/i" `-[; ,<_[>^i_l,:^_! ',+l.. ^,:,,. ;~>l;^ l> ',;I^???~,'l".. .. \s + ;{?l. !+ .. .<1i '^' "}|{:-+-;?\\[)-] ^:l1-:. '' '`, . ';.`~^ '. ..^`. \s + 'i+;]}!,. <))\\!<|ji >((_}}?t)}\\\\v|]?jI!), "lf!l. ... .^ . ". \s + .+{>` l/z\\!,>""I+~_){]vQjut_~~>>>_-<]<-)f":l_v){\\/1}}}{t/\\0?z~. ^' `-l . . .. . \s + l\\ :_>>i:^+\\)_-]!:>-+l'...`^;1!^ 'l>})l\\n\\Qt?]?]})1{][[(XC>^ ... + ^-+^.i-((?!"`:>l<[~<]nQY+?????][{\\cmO||l . ''.. + '|: '^[{~)\\_+++))1{uxnvt(t){{[[u0\\1|({1()){-?|xfc: .. \s + .<-, ]-]]]})11)){{{{}{{)|{}}{1{111{11{{}}]_!"x\\]Xf \s + ,]<\\}][[[[[[[[[[[[[[[]][[[[[[[[][[[[[[]?-+!YC{z} .` + "_[}?]][[[[[[[[[[[[[[[[[[][[[[[[[[[[[[[[[]vn\\?. ^ + ^. ;{_(_??][[[[[[[[[[[[[[[[[|[[[[[[[[[[[[[[[[v_(]^ \s + .' '. :t>/?[[[[[[[[[[[[[[[[[[[]t\\][[[[[[[[[[][]?u;()_ .. .'. . . + `. .` :)!j_]][[[]]]]][[[[[[[[[[}j(/{[[[[[][[}1{~n!)ft . .. .. . .. . + ". ..' .` . "' .^":;~ti{\\1]][]]]??]]]]][]]??[){[}[[[[?+}]!^':``^``,`;I.!<>?>:??i:;-;,<_..^,Ii: 'l,i+```' ..'!; ''. ?~.'<+ .li!:,1?}[[[[[[[[[[[[[[[[[[[[)1}]t[:, "O" . .' . ^!' . ". i+' . \s + ^. . .':`'`~/1,-<-~^'^'^^,`.."i_>^. `1t]!,^I]l^;`,I::_?]?[!:;`.`"'`l!l<1f{~>;]\\1(]I>~l!l[<,,;`lI,~},^>!>l'...'": 'x' ]l>i .:I1[~]]]]}}}}}[[}}[}}}}}[[]]]??1}}[}~;:>vx. :;..:??,.' ^` I;>.";:"^' .. ^'.^"" ' .!},' . + ~!;:!".":i"^_/|]^li(\\1;;it{' .[\\fft+<}(/{}/)|f||'.^{/[)?!:(?+-,I+fjtil"'"+fj{i:',!!;!!^:.`r. r; !l'"i1?!i>~+_?][[[?-???]][[]?-+-]??+~<_{[_l?> ''^l;l`-}<`^.i>+l ``;I":+?!~-l ,>>l.'.;. ':!!(/!":,I + /t1ffft+{jff/ttff)];)?1(/tt\\/t/tfttttfftf/1\\|t\\|/?<_]_]{<_]/f({fffjttf/[i>1//|tft|" :<~:+}, ]>if" .:-~ >) ^`^l)f(_{/\\}-+1\\()t-{j/]!:^'l<]\\)+ ."_?I_{ + ffft)|)(t[_-{tjjrjrj/{(||}(rjj\\1)I<\\((ffj/rjffttjffftrjfffrtfff/f[1jjffffftt//)}tttff/ttt[<{rj}tf1?<:~{/j)>)fttf|?)tfffftt1_;+tf1-1|~i1, >;:} '1_ ;( .. .. `:"_1{}tjtvj)vjr/|jfff/<(tf)+1/)1j)~~-[j[l|[(/\\j{:-]]([}\\t + """; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java index 49e8a08..4059bd0 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java @@ -2,12 +2,32 @@ package cc.sukazyo.cono.morny; import cc.sukazyo.cono.morny.util.FileUtils; +import javax.annotation.Nonnull; import java.net.URISyntaxException; +/** + * Morny Cono 的 Coeur 的程序属性存放类 + */ public class MornySystem { - public static final String VERSION = "0.3.3"; + /** + * 程序的语义化版本号
+ * 会由 gradle 任务 {@code updateVersionCode} 更新 + */ + public static final String VERSION = GradleProjectConfigures.VERSION; + /** + * 获取程序 jar 文件的 md5-hash 值
+ *
+ * 只支持 jar 文件方式启动的程序 —— + * 如果是通过 classpath 来启动,则会返回找不到文件的错误数据
+ * - 或许需要注意,这种情况下会出现程序文件所在的路径
+ *
+ * 值格式为 {@link java.lang.String} + * + * @return 程序jar文件的 md5-hash 值字符串,或错误信息 + */ + @Nonnull public static String getJarMd5() { try { return FileUtils.getMD5Three(MornyCoeur.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java index a1bd6d5..6fa7883 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java @@ -3,12 +3,29 @@ package cc.sukazyo.cono.morny; import com.pengrad.telegrambot.model.ChatMember; import com.pengrad.telegrambot.request.GetChatMember; +/** + * 对用户进行身份权限验证的管理类 + */ public class MornyTrusted { + /** + * 群聊id,其指向的群聊指示了哪个群的成员是受信任的 + * @see #isTrusted(long) 受信检查 + */ public static final long TRUSTED_CHAT_ID = -1001541451710L; + /** + * 用于检查一个 telegram-user 是否受信任
+ *
+ * 用户需要受信任才能执行一些对程序甚至是宿主环境而言危险的操作,例如关闭程序
+ *
+ * 它的逻辑(目前)是检查群聊 {@link #TRUSTED_CHAT_ID} 中这个用户是否为群组管理员 + * + * @param userId 需要检查的用户的id + * @return 所传递的用户id对应的用户是否受信任 + */ 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 ( chatMember != null && ( chatMember.status() == ChatMember.Status.administrator || diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java index 6c5114d..73e4c1a 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListener.java @@ -2,58 +2,60 @@ package cc.sukazyo.cono.morny.bot.api; import com.pengrad.telegrambot.model.Update; +import javax.annotation.Nonnull; + @SuppressWarnings("unused") public abstract class EventListener { - public boolean onMessage (Update update) { + public boolean onMessage (@Nonnull Update update) { return false; } - public boolean onEditedMessage (Update update) { + public boolean onEditedMessage (@Nonnull Update update) { return false; } - public boolean onChannelPost (Update update) { + public boolean onChannelPost (@Nonnull Update update) { return false; } - public boolean onEditedChannelPost (Update update) { + public boolean onEditedChannelPost (@Nonnull Update update) { return false; } - public boolean onInlineQuery (Update update) { + public boolean onInlineQuery (@Nonnull Update update) { return false; } - public boolean onChosenInlineResult (Update update) { + public boolean onChosenInlineResult (@Nonnull Update update) { return false; } - public boolean onCallbackQuery (Update update) { + public boolean onCallbackQuery (@Nonnull Update update) { return false; } - public boolean onShippingQuery (Update update) { + public boolean onShippingQuery (@Nonnull Update update) { return false; } - public boolean onPreCheckoutQuery (Update update) { + public boolean onPreCheckoutQuery (@Nonnull Update update) { return false; } - public boolean onPoll (Update update) { + public boolean onPoll (@Nonnull Update update) { return false; } - public boolean onPollAnswer (Update update) { + public boolean onPollAnswer (@Nonnull Update update) { return false; } - public boolean onMyChatMemberUpdated (Update update) { + public boolean onMyChatMemberUpdated (@Nonnull Update update) { return false; } - public boolean onChatMemberUpdated (Update update) { + public boolean onChatMemberUpdated (@Nonnull Update update) { return false; } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java index 9b59bb1..5ac91a6 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/EventListenerManager.java @@ -2,6 +2,7 @@ package cc.sukazyo.cono.morny.bot.api; import com.pengrad.telegrambot.model.Update; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -17,7 +18,7 @@ public class EventListenerManager { private final Function exec; - public EventPublisher(Update update, Function exec) { + public EventPublisher(@Nonnull Update update, @Nonnull Function exec) { this.setName("EVT"+update.updateId()); 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)); } - public static void publishMessageEvent (Update update) { + public static void publishMessageEvent (@Nonnull Update update) { 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(); } - public static void publishChannelPostEvent (Update update) { + public static void publishChannelPostEvent (@Nonnull Update update) { 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(); } - public static void publishInlineQueryEvent (Update update) { + public static void publishInlineQueryEvent (@Nonnull Update update) { 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(); } - public static void publishCallbackQueryEvent (Update update) { + public static void publishCallbackQueryEvent (@Nonnull Update update) { 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(); } - public static void publishPreCheckoutQueryEvent (Update update) { + public static void publishPreCheckoutQueryEvent (@Nonnull Update update) { 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(); } - public static void publishPollAnswerEvent (Update update) { + public static void publishPollAnswerEvent (@Nonnull Update update) { 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(); } - public static void publishChatMemberUpdatedEvent (Update update) { + public static void publishChatMemberUpdatedEvent (@Nonnull Update update) { new EventPublisher(update, x -> x.onChatMemberUpdated(update)).start(); } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java new file mode 100644 index 0000000..38e4a0b --- /dev/null +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/InputCommand.java @@ -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)); + } + +} diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java b/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java index f45671e..8427283 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/api/OnUpdate.java @@ -3,11 +3,12 @@ package cc.sukazyo.cono.morny.bot.api; import com.pengrad.telegrambot.UpdatesListener; import com.pengrad.telegrambot.model.Update; +import javax.annotation.Nonnull; import java.util.List; public class OnUpdate { - public static int onNormalUpdate (List updates) { + public static int onNormalUpdate (@Nonnull List updates) { for (Update update : updates) { if (update.message() != null) { EventListenerManager.publishMessageEvent(update); diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java index adbd3b3..9c3ed6e 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnActivityRecord.java @@ -5,10 +5,12 @@ import cc.sukazyo.cono.morny.data.tracker.TrackerDataManager; import com.pengrad.telegrambot.model.Chat; import com.pengrad.telegrambot.model.Update; +import javax.annotation.Nonnull; + public class OnActivityRecord extends EventListener { @Override - public boolean onMessage (Update update) { + public boolean onMessage (@Nonnull Update update) { if ( update.message().chat().type() == Chat.Type.supergroup || update.message().chat().type() == Chat.Type.group diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java index 389b329..1db0756 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnCommandExecute.java @@ -1,16 +1,25 @@ package cc.sukazyo.cono.morny.bot.event; +import cc.sukazyo.cono.morny.GradleProjectConfigures; import cc.sukazyo.cono.morny.MornyCoeur; import cc.sukazyo.cono.morny.MornySystem; import cc.sukazyo.cono.morny.MornyTrusted; 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.util.StringUtils; import com.pengrad.telegrambot.model.Update; import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.request.SendSticker; +import javax.annotation.Nonnull; + +import 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; 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"; @Override - public boolean onMessage (Update event) { + public boolean onMessage (@Nonnull Update event) { if (event.message().text() == null) { - return false; + return false; // 检测到无消息文本,忽略掉命令处理 } - String[] command = StringUtils.formatCommand(event.message().text()); - if (command.length == 0) return false; - switch (command[0]) { + final InputCommand command = new InputCommand(event.message().text()); + if (command.getTarget() != null && !MornyCoeur.username.equals(command.getTarget())) { + return true; // 检测到命令并非针对 morny,退出整个事件处理链 + } + switch (command.getCommand()) { case "/user": - case "/user@" + MornyCoeur.USERNAME: - GetUsernameAndId.exec(command, event); + GetUsernameAndId.exec(command.getArgs(), event); break; case "/o": - case "/o@" + MornyCoeur.USERNAME: onCommandOnExec(event); break; case "/hi": - case "/hi@" + MornyCoeur.USERNAME: case "/hello": - case "/hello@" + MornyCoeur.USERNAME: onCommandHelloExec(event); break; case "/exit": - case "/exit@" + MornyCoeur.USERNAME: onCommandExitExec(event); break; case "/version": - case "/version@" + MornyCoeur.USERNAME: onCommandVersionExec(event); break; 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( event.message().chat().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( event.message().chat().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())) { MornyCoeur.getAccount().execute(new SendSticker( 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( event.message().chat().id(), - String.format( - "version:\n" + - "\t%s\n" + - "core md5_hash:\n" + - "\t%s", + String.format(""" + version: + %s + core md5_hash: + %s + compile timestamp: + %d + %s [UTC]""", 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)); } diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java index b43b554..19db7e5 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/OnUserSlashAction.java @@ -8,11 +8,13 @@ import com.pengrad.telegrambot.model.User; import com.pengrad.telegrambot.model.request.ParseMode; import com.pengrad.telegrambot.request.SendMessage; +import javax.annotation.Nonnull; + public class OnUserSlashAction extends EventListener { @Override - public boolean onMessage (Update event) { - String text = event.message().text(); + public boolean onMessage (@Nonnull Update event) { + final String text = event.message().text(); if (text == null) return false; if (text.startsWith("/")) { @@ -31,12 +33,12 @@ public class OnUserSlashAction extends EventListener { prefixLength = 2; } - String[] action = StringUtils.formatCommand(text.substring(prefixLength)); - String verb = action[0]; - boolean hasObject = action.length != 1; - String object = StringUtils.connectStringArray(action, " ", 1, action.length-1); - User origin = event.message().from(); - User target = (event.message().replyToMessage() == null ? ( + final String[] action = StringUtils.formatCommand(text.substring(prefixLength)); + final String verb = action[0]; + final boolean hasObject = action.length != 1; + final String object = StringUtils.connectStringArray(action, " ", 1, action.length-1); + final User origin = event.message().from(); + final User target = (event.message().replyToMessage() == null ? ( origin ): ( event.message().replyToMessage().from() diff --git a/src/main/java/cc/sukazyo/cono/morny/bot/event/on_commands/GetUsernameAndId.java b/src/main/java/cc/sukazyo/cono/morny/bot/event/on_commands/GetUsernameAndId.java index f896e50..b54bb9d 100644 --- a/src/main/java/cc/sukazyo/cono/morny/bot/event/on_commands/GetUsernameAndId.java +++ b/src/main/java/cc/sukazyo/cono/morny/bot/event/on_commands/GetUsernameAndId.java @@ -8,11 +8,13 @@ import com.pengrad.telegrambot.request.GetChatMember; import com.pengrad.telegrambot.request.SendMessage; import com.pengrad.telegrambot.response.GetChatMemberResponse; +import javax.annotation.Nonnull; + 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(), "[Unavailable] Too much arguments." ).replyToMessageId(event.message().messageId())); return; } @@ -22,9 +24,9 @@ public class GetUsernameAndId { if ( event.message().replyToMessage()!= null) { userId = event.message().replyToMessage().from().id(); } - if (command.length > 1) { + if (args.length > 0) { try { - userId = Long.parseLong(command[1]); + userId = Long.parseLong(args[0]); } catch (NumberFormatException e) { MornyCoeur.getAccount().execute(new SendMessage( event.message().chat().id(), @@ -42,7 +44,7 @@ public class GetUsernameAndId { return; } - GetChatMemberResponse response = MornyCoeur.getAccount().execute( + final GetChatMemberResponse response = MornyCoeur.getAccount().execute( new GetChatMember(event.message().chat().id(), userId) ); @@ -54,22 +56,25 @@ public class GetUsernameAndId { 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( - "userid :\n" + - "- %d\n" + - "username :\n" + - "- %s", - userId, user.username() + """ + userid : + - %d + username : + - %s""", + userId, user.username() )); if (user.firstName() == null) { userInformation.append("\nfirstname : null"); } else { userInformation.append(String.format( - "\nfirstname :\n" + - "- %s", + """ + + firstname : + - %s""", user.firstName() )); } @@ -77,15 +82,19 @@ public class GetUsernameAndId { userInformation.append("\nlastname : null"); } else { userInformation.append(String.format( - "\nlastname :\n" + - "- %s", + """ + + lastname : + - %s""", user.lastName() )); } if (user.languageCode() != null) { userInformation.append(String.format( - "\nlanguage-code :\n" + - "- %s", + """ + + language-code : + - %s""", user.languageCode() )); } diff --git a/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java b/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java index 73b17eb..c64a3db 100644 --- a/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java +++ b/src/main/java/cc/sukazyo/cono/morny/util/FileUtils.java @@ -1,6 +1,6 @@ package cc.sukazyo.cono.morny.util; -import java.io.File; +import javax.annotation.Nonnull; import java.io.FileInputStream; import java.io.IOException; import java.math.BigInteger; @@ -9,19 +9,19 @@ import java.security.NoSuchAlgorithmException; public class FileUtils { - public static String getMD5Three (String path) { - BigInteger bi; + @Nonnull + public static String getMD5Three (@Nonnull String path) { + final BigInteger bi; try { - byte[] buffer = new byte[8192]; + final byte[] buffer = new byte[8192]; int len; - MessageDigest md = MessageDigest.getInstance("MD5"); - File f = new File(path); - FileInputStream fis = new FileInputStream(f); + final MessageDigest md = MessageDigest.getInstance("MD5"); + final FileInputStream fis = new FileInputStream(path); while ((len = fis.read(buffer)) != -1) { md.update(buffer, 0, len); } fis.close(); - byte[] b = md.digest(); + final byte[] b = md.digest(); bi = new BigInteger(1, b); } catch (NoSuchAlgorithmException | IOException e) { e.printStackTrace(System.out); diff --git a/src/main/java/cc/sukazyo/cono/morny/util/StringUtils.java b/src/main/java/cc/sukazyo/cono/morny/util/StringUtils.java index 1a31249..7ff40c2 100644 --- a/src/main/java/cc/sukazyo/cono/morny/util/StringUtils.java +++ b/src/main/java/cc/sukazyo/cono/morny/util/StringUtils.java @@ -1,22 +1,26 @@ package cc.sukazyo.cono.morny.util; +import javax.annotation.Nonnegative; +import javax.annotation.Nonnull; import java.util.ArrayList; import java.util.Arrays; public class StringUtils { + @Nonnull public static String repeatChar (char c, int i) { - char[] chars = new char[i]; + final char[] chars = new char[i]; Arrays.fill(chars, c); return new String(chars); } - public static String[] formatCommand (String com) { + @Nonnull + public static String[] formatCommand (@Nonnull String com) { - ArrayList arr = new ArrayList<>(); + final ArrayList arr = new ArrayList<>(); - StringBuilder tmp = new StringBuilder(); - char[] coma = com.toCharArray(); + 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()); } @@ -43,14 +47,17 @@ public class StringUtils { if (!tmp.toString().equals("")) { arr.add(tmp.toString()); } tmp.setLength(0); - String[] out = new String[arr.size()]; + final String[] out = new String[arr.size()]; arr.toArray(out); return out; } - public static String connectStringArray (String[] array, String connector, int startIndex, int stopIndex) { - StringBuilder builder = new StringBuilder(); + @Nonnull + 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++) { builder.append(array[i]); builder.append(connector);