diff --git a/build.gradle b/build.gradle index aa1d3c0..33af0c1 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ plugins { } group 'cc.sukazyo' -version '0.3.4' +version '0.3.4.1' project.ext.archiveBaseName = 'Coeur_Morny_Cono' project.ext.artifactId = 'morny-coeur' mainClassName = 'cc.sukazyo.cono.morny.MornyCoeur' @@ -27,12 +27,14 @@ dependencies { } -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') } } +compileJava.dependsOn updateVersionCode + test { useJUnitPlatform() } diff --git a/src/main/java/cc/sukazyo/cono/morny/Logger.java b/src/main/java/cc/sukazyo/cono/morny/Logger.java index 266f3c4..f48b6d1 100644 --- a/src/main/java/cc/sukazyo/cono/morny/Logger.java +++ b/src/main/java/cc/sukazyo/cono/morny/Logger.java @@ -4,26 +4,71 @@ 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(); + /** + * [INFO] 级别的 log 消息 + * @see #formatMessage(String, String) 输出整理规则 + * @param message 消息文本,支持多行 + */ public void info(@Nonnull String message) { System.out.println(formatMessage(message, "INFO")); } + /** + * [WARN] 级别的 log 消息
+ * {@link #warning(String)} 的重写 + * @see #formatMessage(String, String) 输出整理规则 + * @see #warning(String) 源方法 + * @param message 消息文本,支持多行 + */ public void warn (@Nonnull String message) { - waring(message); + warning(message); } - public void waring (@Nonnull 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")); } + /** + * [ERRO] 级别的 log 消息 + * @see #formatMessage(String, String) 输出整理规则 + * @param message 消息文本,支持多行 + */ public void error (@Nonnull String message) { System.out.println(formatMessage(message, "ERRO")); } + /** + * 将传入的消息和消息元数据重新整理为固定格式
+ *
+ * 这个方法会{@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()); diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java index a35aeaa..8492561 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyCoeur.java @@ -10,11 +10,34 @@ import javax.annotation.Nonnull; import static cc.sukazyo.cono.morny.Logger.logger; +/** + * Morny Cono 核心
+ *
+ * - 的程序化入口类,保管着 morny 的核心属性
+ */ public class MornyCoeur { + /** morny 的 bot 账户 */ private static TelegramBot account; + /** + * morny 的 bot 账户的用户名
+ *
+ * 出于技术限制,这个字段目前是写死的 + */ public static final String USERNAME = "morny_cono_annie_bot"; + /** + * 程序入口
+ *
+ * 会从命令行参数取得初始化数据并初始化程序和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 欢迎标语}而不运行程序逻辑 + * + * @param args 程序命令行参数 + */ public static void main (@Nonnull String[] args) { if (!(args.length > 1 && "--no-hello".equals(args[1]))) @@ -40,15 +63,31 @@ public class MornyCoeur { } + /** + * 用于退出时进行缓存的任务处理等进行安全退出 + */ private static void exitCleanup () { TrackerDataManager.DAEMON.interrupt(); TrackerDataManager.trackingLock.lock(); } + /** + * 为程序在虚拟机上添加退出钩子 + */ private static void configureSafeExit () { Runtime.getRuntime().addShutdownHook(new Thread(MornyCoeur::exitCleanup)); } + /** + * 登录 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); @@ -69,6 +108,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..a054a7e 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyHello.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyHello.java @@ -1,8 +1,14 @@ package cc.sukazyo.cono.morny; +/** + * {@link #MORNY_PREVIEW_IMAGE_ASCII} 静态数据存放类 + */ @SuppressWarnings("all") public class MornyHello { + /** + * 系统的开屏欢迎语 ASCII 字符画字段 + */ 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"; } diff --git a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java index 5de6c7d..e273903 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornySystem.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornySystem.java @@ -5,10 +5,28 @@ 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.4"; + /** + * 程序的语义化版本号
+ * 会由 gradle 任务 {@code updateVersionCode} 更新 + */ + public static final String VERSION = "0.3.4.1"; + /** + * 获取程序 jar 文件的 md5-hash 值
+ *
+ * 只支持 jar 文件方式启动的程序 —— + * 如果是通过 classpath 来启动,则会返回找不到文件的错误数据
+ * - 或许需要注意,这种情况下会出现程序文件所在的路径
+ *
+ * 值格式为 {@link java.lang.String} + * + * @return 程序jar文件的 md5-hash 值字符串,或错误信息 + */ @Nonnull public static String getJarMd5() { try { diff --git a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java index 862fd44..6fa7883 100644 --- a/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java +++ b/src/main/java/cc/sukazyo/cono/morny/MornyTrusted.java @@ -3,10 +3,27 @@ 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) { final ChatMember chatMember = MornyCoeur.getAccount().execute(new GetChatMember(TRUSTED_CHAT_ID, userId)).chatMember(); return (